In-Place Editing with contenteditable

0
124

We’re always looking for ways to make it easier for people to interact with our applications. Sometimes we want a user of our site to edit some information about themselves without having to navigate to a different form. We traditionally implement in-place editing by watching text regions for clicks and replacing those regions with text fields. These fields send the changed text back to the server via Ajax. HTML5’s contenteditable tag takes care of the data-entry part automatically. We’ll  still have to write some JavaScript to send the data back to the server
so we can save it, but we no longer have to create and toggle hidden forms.

One of AwesomeCo’s current projects lets users review their account profile. It displays their name, city, state, postal code, and email address. Let’s add some in-place editing to this profile page so that we end up with an interface like Figure 3.3, on the next page.

Before we get started, I want you to know that implementing a feature that relies on JavaScript without first implementing a server-side solution goes against everything I believe in when it comes to building accessible web applications. We’re doing it this way here because I want to focus on the features of the contenteditable attribute, and this is not production code. Always, and I mean always, build the solution that does not require JavaScript, then build the version that relies on scripting, and finally be sure to write automated tests for both paths so that you’re more likely to catch bugs if you change one version and not the other.

The Profile Form

HTML5 introduces the contenteditable attribute that is available on almost every element. Simply adding this attribute turns it into an editable field.

In-place editing<hl>User information</hl>
<div id=”status”x/div>
<ul>
<li>
<b>Name</b>
<span id=”name” contenteditable=”true”>Hugh Mann</span>
</l i>
<li>
<b>Ci ty</b>
<span id=”city” contenteditable=”true”>Anytown</span>
</l i>
<li>
<b>State</b>
<span id=”state” contenteditable=”true”>OH</span>
</l i>
<li>
<b>Postal Code</b>
<span id=”postal_code” contenteditable=”true”>92110</span>

</l i>

<li>
<b>Email</b>
<span id=”email” contenteditab1e=”true”>[email protected]</span>
</l i>
</u1>

We can style this up with some CSS too. We’ll use some CSS3 selectors to identify the editable fields so they change color when our users hover over or select them.

Line l ul{1ist-style:none;}
1i{clear:both;}
5 li>b, li>span{
display: block;
float: left;
width: lOOpx;
}
li>span{
width:500px;
margin-left: 20px;
}
1i>span[contenteditable=true]:hover{
background-color: #ffc;
}
20 1i>span[contenteditable=true]:focus{
background-color: #ffa;
border: lpx shaded #000;
}

That’s it for the front end. Users can modify the data on the page easily. Now we have to save it.

Persisting the Data

Although the users can change the data, their changes will be lost if they refresh the page or navigate away. We need a way to submit those changes to our back end, and we can do that easily with jQuery. If you’ve ever done any Ajax before, this won’t be anything new to you.

$(function(){
var status = $(“#status”);
$ C”span[contented!table=trueJ”).blur(function 0{
var field = $(this) .attr(“id”) ;
var value = $(this) .text() ;
$ .postC’http://localhost:4567/users/l” ,
field + “=” + value,
function(data){
status.text(data);
}
);
} ) ;
} ) ;

We’ll add an event listener to every span on the page that has the contenteditable attribute set to true. Then, all we have to do is submit the data to our server-side script.

Falling Back

We’ve done a bunch of things that won’t work for some of our audience. First, we’ve created a dependency on JavaScript to save the edited results back to the server, which is a Bad Thing. Next, we’re using the focus pseudoclass to highlight the fields when they receive focus, and
some versions of IE don’t support that. Let’s handle the functionality first, and then we’ll deal with the visual effects.

Creating an Edit Page

Rather than worrying too much about various situations that might prevent a user from using our technique, let’s just give them the option to go to a separate page with its own form. Sure, it’s more coding, but think about the possible scenarios:

• A user doesn’t have JavaScript turned on and is using Internet Explorer 7.
• A user doesn’t have an HTML5-compatible browser.
• A user is using the latest Firefox with HTML5 support but still disabled JavaScript simply because they don’t like JavaScript (it happens all the time…more than you’d think).

When it comes down to it, making a form that does a POST to the same action that handled the Ajax update makes the most sense. How you do this is up to you, but many frameworks let you detect the type of request by looking at the accept headers to determine whether the
request came from a regular POST or an XMLHttpRequest. That way, you keep the server-side code DRY.5 We will hide the link to this form if the browser supports contenteditable and JavaScript.

So, create a new page called edit.html, and code up a standard edit form that posts to the same update action that our Ajax version uses.

<!DOCTYPE html>
<html lang=”en-US”>
<head>
<meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″ />
<title>Edi ti ng Profi 1e</title>
<link href=”style.css” rel=”stylesheet” media=”screen”>
</head>
<body>
<form action=”/users/l” method=”post” accept-charset=”utf-8″>
<fieldset id=”your_information”>
<1egend>Your Information</legend>
<ol>
<li>
<label
<input
</li>
<li>
<label
<input
</li>
<li>
<label
<input
</li>
<li>
<label
<input
</li>
<li>
<label
<input
</li>
</ol>
</fieldset>
<pxinput type=”submit” value=”Save”x/p>
</form>
</body>
</html>

Then, add a link to this page on show.html.

<hl>User information</hl>
<section id=”edit_profile_link”>
< p x a href=”edit.html”>Edit Your Profi 1 e</ax/p>
</section>
<div id=”status”x/div>

With the link added, we just need to modify our script a bit. We want to hide the link to the edit page and enable the Ajax support only if we have support for editable content.

if (document. getElementById(“ech’t_prof”! 7e_7in/c”) . contentEdi tabl e != null){

With the detection in place, our script looks like this:

$(function(){
if (document. getElementById(“ech’t_prof”! 7e_7in/c”) . contentEdi tabl e != null){
$C’#edit_profile_link”) .hideO ;
var status = $(“#status”);
$(“span[contented!table=true]”).blur(function(){
var field = $(this) .attr(“id”) ;
var value = $(this) .text() ;
$ .postC’http://Iocalhost:4567/users/l” ,
field + “=” + value,
function(data){
status.text(data);
}
);

} ) ;

With that in place, our users have the ability to use a standard interface or a quicker “in-place” mode. Now that you know how to implement this interface, remember to implement the fallback solution first. Unlike the other fallback solutions, this particular one cripples functionality if not
implemented.

The Future

Right now, if you add a JavaScript-based date picker to your site, your users have to learn how it works. If you’ve ever shopped online for plane tickets and made hotel reservations, you’re already familiar with the different ways people implement custom form controls on sites. It’s akin to using an ATM—the interface is often different enough to slow you down.

Imagine, though, if each website used the HTML5 date field, and the browser had to create the interface. Each site a user visited would display the exact same date picker. Screen-reading software could even implement a standard mechanism to allow the blind to enter dates easily.
Now think about how useful placeholder text and autofocus can be for users once it’s everywhere. Placeholder text can help screen readers explain to users how form fields should work, and autofocus could help people navigate more easily without a mouse, which is handy for the blind but also for users with motor impairments who may not use the mouse.

The ability for developers to turn any element into an editable region makes it easy to do in-place editing, but it could potentially change how we build interfaces for content  management systems.

The modern Web is all about interactivity, and forms are an essential part of that interactivity. The enhancements provided by HTML5 give us a whole new set of tools we can use to help our users.