Back on day one we looked at using the Prototype library to take all the hard work out of making a simple Ajax call. While that was fun and all, it didn’t go that far towards implementing something really practical. We dipped our toes in, but haven’t learned to swim yet.
So here is swimming lesson number one. Anyone who’s used Flickr to publish their photos will be familiar with the edit-in-place system used for quickly amending titles and descriptions on photographs. Hovering over an item turns its background yellow to indicate it is editable. A simple click loads the text into an edit box, right there on the page.

Prototype includes all sorts of useful methods to help reproduce something like this for our own projects. As well as the simple Ajax GETs we learned how to do last time, we can also do POSTs (which we’ll need here) and a whole bunch of manipulations to the user interface – all through simple library calls. Here’s what we’re building, so let’s do it.
Getting Started
There are two major components to this process; the user interface manipulation and the Ajax call itself. Our set-up is much the same as last time (you may wish to read the first article if you’ve not already done so). We have a basic HTML page which links in the prototype.js file and our own editinplace.js. Here’s what Santa dropped down my chimney:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Edit-in-Place with Ajax</title>
<link href="editinplace.css" rel="Stylesheet" type="text/css" />
<script src="prototype.js" type="text/javascript"></script>
<script src="editinplace.js" type="text/javascript"></script>
</head>
<body>
<h1>Edit-in-place</h1>
<p id="desc">Dashing through the snow on a one horse open sleigh.</p>
</body>
</html>
So that’s our page. The editable item is going to be the <p> called desc. The process goes something like this:
- Highlight the area
onMouseOver - Clear the highlight
onMouseOut - If the user clicks, hide the area and replace with a
<textarea>and buttons - Remove all of the above if the user cancels the operation
- When the Save button is clicked, make an Ajax POST and show that something’s happening
- When the Ajax call comes back, update the page with the new content
Events and Highlighting
The first step is to offer feedback to the user that the item is editable. This is done by shading the background colour when the user mouses over. Of course, the CSS :hover pseudo class is a straightforward way to do this, but for three reasons, I’m using JavaScript to switch class names.
:hoverisn’t supported on many elements in Internet Explorer for Windows- I want to keep control over when the highlight switches off after an update, regardless of mouse position
- If JavaScript isn’t available we don’t want to end up with the CSS suggesting it might be
With this in mind, here’s how editinplace.js starts:
Event.observe(window, 'load', init, false);function init(){ makeEditable('desc'); }function makeEditable(id){ Event.observe(id, 'click', function(){edit($(id))}, false); Event.observe(id, 'mouseover', function(){showAsEditable($(id))}, false); Event.observe(id, 'mouseout', function(){showAsEditable($(id), true)}, false); }function showAsEditable(obj, clear){ if (!clear){ Element.addClassName(obj, 'editable'); }else{ Element.removeClassName(obj, 'editable'); } }
The first line attaches an onLoad event to the window, so that the function init() gets called once the page has loaded. In turn, init() sets up all the items on the page that we want to make editable. In this example I’ve just got one, but you can add as many as you like.
The function madeEditable() attaches the mouseover, mouseout and click events to the item we’re making editable. All showAsEditable does is add and remove the class name editable from the object. This uses the particularly cunning methods Element.addClassName() and Element.removeClassName() which enable you to cleanly add and remove effects without affecting any styling the object may otherwise have.
Oh, remember to add a rule for .editable to your style sheet:
.editable{
color: #000;
background-color: #ffffd3;
}
The Switch
As you can see above, when the user clicks on an editable item, a call is made to the function edit(). This is where we switch out the static item for a nice editable textarea. Here’s how that function looks.
function edit(obj){ Element.hide(obj);var textarea ='<div id="' + obj.id + '_editor"> <textarea id="' + obj.id + '_edit" name="' + obj.id + '" rows="4" cols="60">' + obj.innerHTML + '</textarea>';var button = '<input id="' + obj.id + '_save" type="button" value="SAVE" /> OR <input id="' + obj.id + '_cancel" type="button" value="CANCEL" /></div>';new Insertion.After(obj, textarea+button);Event.observe(obj.id+'_save', 'click', function(){saveChanges(obj)}, false); Event.observe(obj.id+'_cancel', 'click', function(){cleanUp(obj)}, false);}
The first thing to do is to hide the object. Prototype comes to the rescue with Element.hide() (and of course, Element.show() too). Following that, we build up the textarea and buttons as a string, and then use Insertion.After() to place our new editor underneath the (now hidden) editable object.
The last thing to do before we leave the user to edit is it attach listeners to the Save and Cancel buttons to call either the saveChanges() function, or to cleanUp() after a cancel.
In the event of a cancel, we can clean up behind ourselves like so:
function cleanUp(obj, keepEditable){
Element.remove(obj.id+'_editor');
Element.show(obj);
if (!keepEditable) showAsEditable(obj, true);
}
Saving the Changes
This is where all the Ajax fun occurs. Whilst the previous article introduced Ajax.Updater() for simple Ajax calls, in this case we need a little bit more control over what happens once the response is received. For this purpose, Ajax.Request() is perfect. We can use the onSuccess and onFailure parameters to register functions to handle the response.
function saveChanges(obj){ var new_content = escape($F(obj.id+'_edit'));obj.innerHTML = "Saving..."; cleanUp(obj, true);var success = function(t){editComplete(t, obj);} var failure = function(t){editFailed(t, obj);}var url = 'edit.php'; var pars = 'id=' + obj.id + '&content=' + new_content; var myAjax = new Ajax.Request(url, {method:'post', postBody:pars, onSuccess:success, onFailure:failure}); }function editComplete(t, obj){ obj.innerHTML = t.responseText; showAsEditable(obj, true); }function editFailed(t, obj){ obj.innerHTML = 'Sorry, the update failed.'; cleanUp(obj); }
As you can see, we first grab in the contents of the textarea into the variable new_content. We then remove the editor, set the content of the original object to “Saving…” to show that an update is occurring, and make the Ajax POST.
If the Ajax fails, editFailed() sets the contents of the object to “Sorry, the update failed.” Admittedly, that’s not a very helpful way to handle the error but I have to limit the scope of this article somewhere. It might be a good idea to stow away the original contents of the object (obj.preUpdate = obj.innerHTML) for later retrieval before setting the content to “Saving…”. No one likes a failure – especially a messy one.
If the Ajax call is successful, the server-side script returns the edited content, which we then place back inside the object from editComplete, and tidy up.
Meanwhile, back at the server
The missing piece of the puzzle is the server-side script for committing the changes to your database. Obviously, any solution I provide here is not going to fit your particular application. For the purposes of getting a functional demo going, here’s what I have in PHP.
<?php
$id = $_POST['id'];
$content = $_POST['content'];
echo htmlspecialchars($content);
?>
Not exactly rocket science is it? I’m just catching the content item from the POST and echoing it back. For your application to be useful, however, you’ll need to know exactly which record you should be updating. I’m passing in the ID of my <div>, which is not a fat lot of use. You can modify saveChanges() to post back whatever information your app needs to know in order to process the update.
You should also check the user’s credentials to make sure they have permission to edit whatever it is they’re editing. Basically the same rules apply as with any script in your application.
Limitations
There are a few bits and bobs that in an ideal world I would tidy up. The first is the error handling, as I’ve already mentioned. The second is that from an idealistic standpoint, I’d rather not be using innerHTML. However, the reality is that it’s presently the most efficient way of making large changes to the document. If you’re serving as XML, remember that you’ll need to replace these with proper DOM nodes.
It’s also important to note that it’s quite difficult to make something like this universally accessible. Whenever you start updating large chunks of a document based on user interaction, a lot of non-traditional devices don’t cope well. The benefit of this technique, though, is that if JavaScript is unavailable none of the functionality gets implemented at all – it fails silently. It is for this reason that this shouldn’t be used as a complete replacement for a traditional, universally accessible edit form. It’s a great time-saver for those with the ability to use it, but it’s no replacement.
See it in action
I’ve put together an example page using the inert PHP script above. That is to say, your edits aren’t committed to a database, so the example is reset when the page is reloaded.


Comments
Comments are ordered by helpfulness, as indicated by you. Help us pick out the gems and discourage asshattery by voting on notable comments.
Got something to add? You can leave a comment below.
23/12/2005
Thanks so much for this!
I needed it for a project, and I was trying to figure out the most efficient way to go about solving the problem.
Cheers!
Vote Helpful or Unhelpful
23/12/2005
Incredible example. Thank you so much for this.
Vote Helpful or Unhelpful
23/12/2005
Very nicely done!
Vote Helpful or Unhelpful
23/12/2005
Great Tutorial. This is JUST what I was looking for in my next project!
Vote Helpful or Unhelpful
23/12/2005
Cool stuff Drew. This reminds of PPK’s edit in place idea for a CMS…although, he never actually did anything with it except save the changes into the DOM memory. This…actually does something! Great work man.
Vote Helpful or Unhelpful
23/12/2005
Too good. Very nice article and neatly explained. thats really of u.
Vote Helpful or Unhelpful
23/12/2005
Very good article, easily explained and a good example
Thanks!
Vote Helpful or Unhelpful
23/12/2005
wow.. great idea.
Vote Helpful or Unhelpful
23/12/2005
cÔÔl !
I think you need to CSS-style the TextArea though, so that in place editing isn’t too confusing (i.e. clicking a word and then needing to go up or down a line, depending on the new layout to change it).
You are mentionning finding a way to Highlight the fact you did change something, but the font switch is too much a change, I think.(as seen with FireFox 1.5 at least)
I definitely think we should find an idea to pursue this ‘Impress your friends’ serie beyond the 24th.
Vote Helpful or Unhelpful
23/12/2005
found some very cool tutorials on this site. thanks for all the work you put into this! the question is though… what happens on the 25th? is all that wonderful content just going to disappear? or are you maybe planning on zipping all the tutorials and sample files up into one handy package for people to download? enquiering minds want to know…
Vote Helpful or Unhelpful
23/12/2005
Now this was interesting article. Good work! I’ll have to start playing with it.
Vote Helpful or Unhelpful
23/12/2005
Good tutorial. I recently got inspired by Flickr too, to do a similar thing in a CMS.
One thing that could be done to improve it is to hook it into a form. For instance if it was a text input field not a textarea (i.e. for a title) pressing return would automatically submit without having to do extra coding. It could also be a fall-back if the ajax call failed or wasn’t supported in a particular browser.
Vote Helpful or Unhelpful
23/12/2005
Nice little explaination. Prototype is pretty massive and daunting task if you don’t know JavaScript very well.
When I first looked into copying the Flickr experience I found this edit in place function, but it didn’t have the AJAX element.
In the end I made it up myself, it works, but probably not as well as this.
As for XML and innerHTML. FF1.5 now supports it when sending pages as XML. Good or bad, I haven’t yet decided.
Vote Helpful or Unhelpful
23/12/2005
Thx Drew – this is probably the best christmas gift for all of us :-)
Merry Christmas
Vote Helpful or Unhelpful
23/12/2005
ooohh this is sweeet! thank you!
..and yes, Merry Christmas!
Vote Helpful or Unhelpful
23/12/2005
Very slick. One issue I ran into was when I placed and named html entity like & or …, it didn’t save it as I typed it in. Is there a way to escape the ampersand?
Vote Helpful or Unhelpful
23/12/2005
pretty cool! i was wondering, say i wanted to run the cleanup function just by clicking outside of the textarea, how would i go about that. i tried attaching to the body tag, but i couldn’t get it to work… thx!
Vote Helpful or Unhelpful
23/12/2005
db: You may wish to rethink your goal. What would happen if a user clicked outside of the edit box part way through making an edit? (e.g. they may be switching between windows, copying and pasting from elsewhere).
Vote Helpful or Unhelpful
23/12/2005
that’s true, but i wouldn’t use it for large amounts of text. more like titles or tags, as in flickr or del.icio.us… but that’s a good point.
Vote Helpful or Unhelpful
23/12/2005
I’d like to see “nl2br” integrated and html as well.
You know what would really be cool, integrate TinyMCE or FCKeditor into the edit screen – that would rock.
Good job though – nice exp.
Vote Helpful or Unhelpful
23/12/2005
xvrd: I have no plans to remove any content published here this month. All the URLs can be considered permanent. (If I do reshuffle for whatever reason, I’ll make sure the links keep working).
Vote Helpful or Unhelpful
23/12/2005
You can also use the Ajax.InPlaceEditor that is from http://script.aculo.us that simplifies some of this…
Vote Helpful or Unhelpful
24/12/2005
Once again thanks for the great article!
Vote Helpful or Unhelpful
24/12/2005
Thanks for the great tut, cant seem to get the function to saving the text at all, where does the php go?
Vote Helpful or Unhelpful
25/12/2005
is there anyone with a working version of this that has their source code that i could some how download? i have done this tutorial about 3 times now and NOTHING on it is working. i’m an experienced mysql/php web designer but new to this whole ajax thing and just trying out some demos here and there. i got the other ajax demo to work just fine, but all this one does is display a title and a paragraph and nothing is editable or changes colors on mouse over or anything. any help? thanks.
Vote Helpful or Unhelpful
26/12/2005
Interesting tutorial – I’ll have to give it a try.
Vote Helpful or Unhelpful
27/12/2005
eli, I have a working example of something similar to this in my own cms. goto the admin area, log in as “demo” “demo”, click “pages”, then “sample events page”. you can edit the news easily by simply clicking on what you want to change. it also has a wysiwyg built-in (fckeditor)
Vote Helpful or Unhelpful
27/12/2005
kae verens, i checked your example, and it seems to be working as you said, but do you have a working example of THIS TUTORIAL? i need to take a look at the source file and see what is wrong with mine. anyone else have any help? did you get this tutorial to work? thanks.
Vote Helpful or Unhelpful
28/12/2005
eli: Err, but there is one right here on this site.
Vote Helpful or Unhelpful
28/12/2005
I got it working, although I decided to use className instead because it was a list of stuff I wanted editable.
Great job. Made my life a bit easier.
Vote Helpful or Unhelpful
28/12/2005
function init(){
var max = 50;
for (i = 1; i lt= max; i++){
makeEditable(‘item_’+i);}
}
Replace lt with the less-than symbol. Use this in place of the existing init() function in editinplace.js for those multiple item pages you have; dynamically generated of course :-)
Vote Helpful or Unhelpful
29/12/2005
eli, make sure you download the other files in the include:
http://24ways.org/examples/edit-in-place-with-ajax/editinplace.css
http://24ways.org/examples/edit-in-place-with-ajax/prototype.js
http://24ways.org/examples/edit-in-place-with-ajax/editinplace.js
Vote Helpful or Unhelpful
30/12/2005
Awesome! Love it, love the well thought explanation; very consise and very readable and ultimately lowers the hurdle more than any others on AJAX which myn eyes have read today or previously.
I do NOT like/use/no PHP, but dig how it can be used in place of ActiveX for posting w/o refresh – which is the ultimate end game issue. Do you have any excellent redirections to how-to w/o PHP?
Here are typos:
1) make:
“The function madeEditable() attaches the mouseover…”
should say:
makeEditable instead of madeEditable.
2) , false
“Event.observe(obj.id+’_cancel’, ‘click’, function(){cleanUp(obj)}, false);”
should have a ”, false)” parameter in the call to cleanUp as in:
Event.observe(obj.id+’_cancel’, ‘click’, function(){cleanUp(obj,false)}, false);
Vote Helpful or Unhelpful
31/12/2005
Thx for a great example – just what I was looking for in a new project. Good informing tutorial.
Vote Helpful or Unhelpful
02/01/2006
I think the problem with this script is that text “Saving…” when you click on Save button should not replace editable content.
For example, I’ve edited content, then killed my Internet connection and click on Save. In the place of edited text I get “Saving…”. Because the Internet connection is broken (or Work Offline is selected on browser), next click on editable DIV brings me text “Saving…” to edit, and the previous content is lost.
Vote Helpful or Unhelpful
02/01/2006
There are several components for JSP
in Coldtags suite http://www.servletsuite.com/jsp.htm
that provides the same
Vote Helpful or Unhelpful
03/01/2006
Thanx a lot!
I’ve added:
document.getElementById(obj.id+'_edit').focus();at the end of the
edit-function to focus on that field, maybe handy for other users..Vote Helpful or Unhelpful
03/01/2006
I try replacing function init() for a page with multiple items with:-
function init(){
var max = 50;
for (i = 1; i lt= max; i++){
makeEditable(‘item_’+i);}
}
but when i run the script i get the error “expected ’;’ on line 7” which equates to the “for” line.
Does anyone have any ideas or suggestions as to whythis doesnt work?
Thanks, Andrew
Vote Helpful or Unhelpful
03/01/2006
Great article! I’m currently working on a project that needs some inline editing – however, there’s an additional kicker in that some editables are not text-only. Within the rails app, the editor needs to support custom “read” and “write” partials to dump into the editor form.
I’ve implimented it using a regex to properly escape any quotes in the partials and dumping them into the onclick call on the editable element.
Early days yet but so far it looks like this:
http://files.angryamoeba.co.uk/crap/tails-editors.png
couple of points:-
1. I’m using position: absolute because some editable elements are in small screen areas that will not contain a form in a presentable manner.
2. I’m fading down the opacity on the edited element for additional feedback.
Hope that’s of use to someone out there. Keep up the great work with this site.
Vote Helpful or Unhelpful
04/01/2006
How would you go about changing the ID elements to classes so that you could make more then one unit editable? I cant seem to figure this out.
Vote Helpful or Unhelpful
05/01/2006
It’s a super article! I have it working, but I’m busy to implement TinyMCE or FCKeditor, but it don’t work. Could somebody explain me, how I make it? Sorry for my bad english.
Vote Helpful or Unhelpful
06/01/2006
function init(){
var max = 50;
for (i = 1; i < max; i++){
makeEditable(‘item_’+i);}
}
it worked for me Andrew.
nice article !!
Vote Helpful or Unhelpful
08/01/2006
Please could someone email me a working copy of the files to allow editing of mutliple items ‘made editable’ via the finction discussed above.
Please send to andrew[dot]bean[at]gmail.com.
Thanks in advacne,
Andrew
Vote Helpful or Unhelpful
09/01/2006
First of all, GREAT SCRIPT!!
I got it working with multiple items. The only thing I want to do is send an extra id to the edit.php file. So I can use it to update a single row in a mysql database.
Someone got an idea?
Greetings Arnold
Vote Helpful or Unhelpful
10/01/2006
That’s so great !
This is a excelent site to learn more.
Thank you
Vote Helpful or Unhelpful
11/01/2006
Indeed this is a great tutorial although one of some problems is apparent. For example, after editing content and clicking on “Save”, you get text “Saving…” which is also editable (!!) whereas it shouldn’t be. Can you fix that? (I’m talking about your example page)
Vote Helpful or Unhelpful
15/01/2006
Best edit-in-place tutorial I’ve seen yet. Nice work.
Vote Helpful or Unhelpful
16/01/2006
I would change htmlspecialchars($content) for stripslashes(htmlentities($content))
This way, it adds suport for quotes and foreign languages
Vote Helpful or Unhelpful
17/01/2006
GAh! This is soo cool but I don’t know any php so I have no idea how to actually save what I modify. Could somone post or e-mail a php script/method that will allow me to save changes?
Vote Helpful or Unhelpful
19/01/2006
Had tested it in Zope, and works like a charm!
Really interesting site.
to:Kae Verens – WOW, your cms seems very cool, good job
Vote Helpful or Unhelpful
Impress us