打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Edit-in-Place with Ajax

Back on day one we looked at using the Prototype libraryto take all the hard work out of making a simple Ajax call. While thatwas fun and all, it didn’t go that far towards implementing somethingreally practical. We dipped our toes in, but haven’t learned to swimyet.

So here is swimming lesson number one. Anyone who’s used Flickrto publish their photos will be familiar with the edit-in-place systemused for quickly amending titles and descriptions on photographs.Hovering over an item turns its background yellow to indicate it iseditable. A simple click loads the text into an edit box, right thereon the page.

Prototypeincludes all sorts of useful methods to help reproduce something likethis for our own projects. As well as the simple Ajax GETs we learnedhow to do last time, we can also do POSTs (which we’ll need here) and awhole bunch of manipulations to the user interface – all through simplelibrary calls. Here’s what we’re building, so let’s do it.

Getting Started

Thereare two major components to this process; the user interfacemanipulation and the Ajax call itself. Our set-up is much the same aslast 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:

  1. Highlight the area onMouseOver
  2. Clear the highlight onMouseOut
  3. If the user clicks, hide the area and replace with a <textarea> and buttons
  4. Remove all of the above if the user cancels the operation
  5. When the Save button is clicked, make an Ajax POST and show that something’s happening
  6. When the Ajax call comes back, update the page with the new content

Events and Highlighting

Thefirst step is to offer feedback to the user that the item is editable.This is done by shading the background colour when the user mousesover. 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.

  1. :hover isn’t supported on many elements in Internet Explorer for Windows
  2. I want to keep control over when the highlight switches off after an update, regardless of mouse position
  3. 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. Inthis 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 controlover 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 Ihave to limit the scope of this article somewhere. It might be a goodidea 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.

Ifthe Ajax call is successful, the server-side script returns the editedcontent, which we then place back inside the object from editComplete, and tidy up.

Meanwhile, back at the server

Themissing piece of the puzzle is the server-side script for committingthe changes to your database. Obviously, any solution I provide here isnot going to fit your particular application. For the purposes ofgetting 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 contentitem from the POST and echoing it back. For your application to beuseful, however, you’ll need to know exactly which record you should beupdating. 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.

Youshould also check the user’s credentials to make sure they havepermission to edit whatever it is they’re editing. Basically the samerules apply as with any script in your application.

Limitations

Thereare a few bits and bobs that in an ideal world I would tidy up. Thefirst is the error handling, as I’ve already mentioned. The second isthat from an idealistic standpoint, I’d rather not be using innerHTML.However, the reality is that it’s presently the most efficient way ofmaking large changes to the document. If you’re serving as XML,remember that you’ll need to replace these with proper DOM nodes.

It’salso important to note that it’s quite difficult to make something likethis universally accessible. Whenever you start updating large chunksof a document based on user interaction, a lot of non-traditionaldevices don’t cope well. The benefit of this technique, though, is thatif JavaScript is unavailable none of the functionality gets implementedat 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 pageusing the inert PHP script above. That is to say, your edits aren’tcommitted to a database, so the example is reset when the page isreloaded.

About the author

Drew McLellan is a web developer, author and no-good swindler from just outside London, England. At the Web Standards Project he works on press, strategy and tools. Drew keeps a personal weblog covering web development issues and themes.

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
缓存算法 | LiXiang
Simplify Ajax development with jQuery
成功vue动态表单支持多种控件
ABAP和JavaScript的懒加载,单例和桥接模式的实现和比较
jQuery ajax - serialize() 方法
jquery textext
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服