19

I'm trying to make a page that has some editabable fields, but I only want them to display as input boxes once the user clicks on them (the rest of the time showing as plain text). Is there a simple way to do this in Javascript?

2
  • If the user focuses other things, should they revert into plain text again? Commented Jul 25, 2011 at 9:27
  • I was going to put a "save changes" button at the bottom of the page, so it can probably stay as an input field Commented Jul 25, 2011 at 9:31

3 Answers 3

42

Introduction

Fairly simple, yes. I can think of two basic approaches:

  • Using the contenteditable attribute
  • Using an input you add on-the-fly

Handy references for both of the below:

Using the contenteditable attribute

The contentEditable attribute (W3C, MDC, MSDN) can be "true" indicating that the element can be edited directly. This has the advantage of not requiring any JavaScript at all (live example):

<p id="container">The <span contenteditable="true">colored items</span> in this paragraph
are <span contenteditable="true">editable</span>.</p>

Lest you think this is some l33t new thing, IE has supported it since IE 5.5 and other major browsers for very nearly that long. (In fact, this was one of many Microsoft innovations from the IE5.5 / IE6 timeframe; they also gave us innerHTML and Ajax.)

If you want to grab the (edited) content, you just grab innerHTML from the elements you've made editable. Here's an example of some JavaScript that will flag up when contenteditable spans blur (live copy):

var spans = document.getElementsByTagName("span"),
    index,
    span;

for (index = 0; index < spans.length; ++index) {
    span = spans[index];
    if (span.contentEditable) {
        span.onblur = function() {
            var text = this.innerHTML;
            text = text.replace(/&/g, "&amp").replace(/</g, "&lt;");
            console.log("Content committed, span " +
                    (this.id || "anonymous") +
                    ": '" +
                    text + "'");
        };
    }
}
#container span {
    background-color: #ff6;
}
<p id="container">The <span id="span1" contenteditable="true">colored items</span> in this paragraph 
    are <span contenteditable="true">editable</span>.</p>

Using an input you add on-the-fly

You need to get a reference to the element that you're using for display (a span, perhaps) and then hook its click event (or hook the click event on a parent of the desired element(s)). In the click event, hide the span and insert a input[type=text] alongside it.

Here's a very simple example of using an input:

window.onload = function() {
    document.getElementById('container').onclick = function(event) {
        var span, input, text;

        // Get the event (handle MS difference)
        event = event || window.event;

        // Get the root element of the event (handle MS difference)
        span = event.target || event.srcElement;

        // If it's a span...
        if (span && span.tagName.toUpperCase() === "SPAN") {
            // Hide it
            span.style.display = "none";

            // Get its text
            text = span.innerHTML;

            // Create an input
            input = document.createElement("input");
            input.type = "text";
            input.value = text;
            input.size = Math.max(text.length / 4 * 3, 4);
            span.parentNode.insertBefore(input, span);

            // Focus it, hook blur to undo
            input.focus();
            input.onblur = function() {
                // Remove the input
                span.parentNode.removeChild(input);

                // Update the span
                span.innerHTML = input.value == "" ? "&nbsp;" : input.value;

                // Show the span again
                span.style.display = "";
            };
        }
    };
};
#container span {
    background-color: #ff6;
}
<p id="container">The <span>colored items</span> in this paragraph
    are <span>editable</span>.</p>

There I'm hooking the click on the parent p element, not the individual spans, because I wanted to have more than one and it's easier to do that. (It's called "event delegation.") You can find the various functions used above in the references I gave at the beginning of the answer.

In this case I used blur to take the edit down again, but you may wish to have an OK button and/or other triggers (like the Enter key).


Off-topic: You may have noticed in the JavaScript code above that I had to handle a couple of "MS differences" (e.g., things that IE does differently from other browsers), and I've used the old "DOM0" style of event handler where you just assign a function to a property, which isn't ideal, but it avoids my having to handle yet another difference where some versions of IE don't have the DOM2 addEventListener and so you have to fall back to attachEvent.

My point here is: You can smooth over browser differences and get a lot of utility functions as well by using a decent JavaScript library like jQuery, Prototype, YUI, Closure, or any of several others. You didn't say you were using any libraries, so I didn't in the above, but there are compelling reasons to use them so you don't have to worry about all the little browser niggles and can just get on with addressing your actual business need.

Sign up to request clarification or add additional context in comments.

4 Comments

An example of exactly this using MochiKit framework can be found here.
How would I add the name of the input so that the script that processes the form can access the new data?
@Philbert: If you're doing the input element thing (the second item), you just assign it to the name property. Just after the input.type = "text"; line, put input.name = "the name you want";. You might take that name from an attribute on the source element. If so, I'd use a data-* attribute for that for maximum compatibility and future-proofing. E.g., <span data-name="foo"> and then input.name = span.getAttribute("data-name");.
@Philbert: If using the contenteditable approach, I'd just use a data-* attribute that your code processing the changes looks for directly.
3

A trivial example using plain JavaScript would be along the lines of: http://jsfiddle.net/vzxW4/.

document.getElementById('test').onclick = function() {
    document.body.removeChild(this);
    var input = document.createElement('input');
    input.id = 'test';
    input.value = this.innerHTML;
    document.body.appendChild(input);
    input.select();
}

Using a library would save you time and headaches, though. For example, using jQuery: http://jsfiddle.net/vzxW4/1/.

$("#test").click(function() {
    var input = $("<input>", { val: $(this).text(),
                               type: "text" });
    $(this).replaceWith(input);
    input.select();
});

Comments

1

Can we do it simple guys?

Just keep textbox with readonly property true and some CSS which makes text box looks like span with border.

Then as soon as user clicks on text box remove readonly attribute.

On blur restore the CSS and readonly attributes.

5 Comments

Not a bad way to go, although of course when not being edited, the content can't word-wrap. Swapping in an input on-the-fly isn't complicated.
We should respect the principle of KISS en.wikipedia.org/wiki/KISS_principle
@ Jatin: I think we're all familiar with KISS. I disagree it necessarily carries the day here. (contenteditable is even simpler than a read-only, styled-to-within-an-inch-of-its-life input element.)
contenteditable looks like it'll do what I need from a user point of view, I just need to save that back to the form (and database)
Content editable is buggy and should only be used if necessary for rich text editing etc. Mdn mark it deprecated. This approach seems simplest. I don't see why you would need to switch to and from read only though this could be pure CSS

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.