Web developers are a demanding lot. We’re impatient for change, we wanted it yesterday, and we love shortcuts. I don’t mean the “cutting corners” type of shortcut. I mean the “this will make life easier” type of shortcut.
In this post, I’ll show you how you can use AJAX to edit your ExpressionEngine content on the front end, without a page refresh, and with a simple click of the mouse.
NOTE: I recently wrote about an improved method for inline editing with EE using modal boxes. Read the tutorial here.
The Problem
Making changes to front end content can be time consuming and confusing. Even if your templates are set to display WordPress-style “edit” links next to each weblog entry, you have to use the control panel (CP) to make changes. Many end users don’t feel comfortable using the CP, and many times you just want to make a quick change without leaving the front end.
The good news is that with a bit of AJAX, we can bypass the CP altogether, greatly simplifying the editing process.
The Solution
I took my inspiration from an old post on inline editing at 24 ways. I’ve modified the code to work with ExpressionEngine, and will show you how you can get it to work with your own EE site.
Step 1: Create a new EE weblog and template
Create a new weblog and connect it to a custom field group with at least one textarea field (make sure the formatting is set to “none”). Enter a few sample entries, and then create a new template.
Enter the following HTML in your template, substituting your weblog name for ajax-test and your custom field name for {body}:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Edit-in-Place with Ajax</title>
<link rel="stylesheet" type="text/css" href="{stylesheet=home/stylesheet}" />
</head>
<body>
<h1>Editing Inline with ExpressionEngine and AJAX</h1>
{if logged_in}
{exp:weblog:entries disable="categories|member_data|pagination|trackbacks" weblog="ajax-test"}
<h2>{title}</h2>
<p>{body}</p>
{/exp:weblog:entries}
{/if}
{if logged_out}<p>You must be logged in to view this page!</p>{/if}
</body>
</html>
I recommend previewing your template to ensure the entries are displaying properly.
Step 2: Grab the Prototype JavaScript library
The script we’ll be using requires Prototype. Get the latest version of Prototype here, upload it to your server, and add the following line to your template (inside the <head> tags):
<script src="http://path-to-your-scripts-folder/prototype.js" type="text/javascript"></script>
Step 3: Grab the Script
Here’s where all the magic happens. Grab the script here and save it as editinplace.js (don’t upload it just yet). Be sure to place a call to the script in your template by adding this line:
<script src="http://path-to-your-scripts-folder/editinplace.js" type="text/javascript"></script>
I recommend leaving the script open in another window while you follow along here.
Step 4: Get familiar with the script
Let’s take a closer look at editinplace.js. The first line ensures the script won’t run until the page is fully loaded. The init() function defines which element ID we want to be editable – in this case, editTitle.
The makeEditable() function does three things:
- On mouseclick, call the
edit()function, which hides the editable text and displays a textarea and two buttons (“Save” and “Cancel”) - On mouseover, add a CSS class to the editable area (this allows us to highlight it on hover)
- On mouseout, remove the CSS class from the editable area
Notice that the showAsEditable() function is creating and removing a CSS class called editable. You can rename this to whatever you want, but be sure to add a declaration to your stylesheet.
The declaration I’m using on the demo page is:
.editable { background:#ffffd3; cursor:pointer; }
If a user clicks “Cancel,” the cleanUp() function is called; it clears the textarea and buttons and unhides the original text.
If a user clicks “Save,” the saveChanges() function is called; it posts the changes, displays a “Saving…” message, and returns the updated text.
Step 5: Grab the PHP
The script you just copied works in tandem with a small PHP script, which takes your changes and echoes them to the screen. Paste the script below into a new file:
<?php
$id = $_POST['id'];
$content = $_POST['content'];
echo htmlspecialchars($content);
?>
Save this file as edit.php and upload it to your server. Update editinplace.js (line 53) to point to your PHP script, then upload this file too.
Step 6: Put the script to work
Now that we know what the scripts are doing, let’s put them to work. Editinplace.js is looking for an element with an ID of editTitle, but your template doesn’t currently have that. Let’s add it in.
Change the <h2> tag in your template to this:
<h2 id="editTitle">{title}</h2>
Save your template and preview it. Move your mouse over your first heading. Assuming you copied my CSS rules from above, you should see a highlighted box surround your heading, and your cursor should change to a pointer. Click the heading, edit the text, and click “Save” or “Cancel.”
If you try to edit your other headings, you’ll notice nothing’s happening.
Why is this? Remember that an ID is unique to a page, so if you have duplicate IDs, you’ll get strange results. In this case, only the first element of a given ID will be editable.
For most ExpressionEngine applications, you’ll be looping through a weblog several times, so this approach won’t work.
Step 7: Making multiple items on the same page editable
First, change the init() function in editinplace.js to this:
function init() {
var max = 50;
for (var i = 1; i <= max; i++) {
makeEditable('editTitle_' + i);
}
}
Then, in your template, modify your <h2> tag like so:
<h2 id="editTitle_{count}">{title}</h2>
Refresh your template and you should be able to make changes to all of your headings.
Step 8: Posting changes to your ExpressionEngine database
Up to this point, we’ve only been echoing changes back to the screen. Now it’s time to make those changes permanent by posting them to a database.
The bulk of the work will come in edit.php, though before we start making changes to that file, we need to add some things to editinplace.js.
Update your saveChanges() function to look like this (changes are in bold):
function saveChanges(obj) {
var new_content = escape($F(obj.id+'_edit'));
obj.preUpdate = obj.innerHTML
obj.innerHTML = "Saving…";
cleanUp(obj, true);
var success = function(t){editComplete(t, obj);}
var failure = function(t){editFailed(t, obj);}
var url = 'http://path-to-your-scripts-folder/edit.php';
var pars = 'id=' + obj.id + '&content=' + new_content + '&pre=' + obj.preUpdate;
var myAjax = new Ajax.Request(url, {method:'post',
postBody:pars, onSuccess:success, onFailure:failure});
}
If you copy and paste from above, don’t forget to update the path to your PHP file.
Next, update edit.php to look like this:
<?php
$id = $_POST['id'];
$content = $_POST['content'];
$pre = $_POST['pre'];
echo htmlspecialchars($content);
require_once("path-to-your/include.inc.php");
$sql = mysql_query("UPDATE exp_weblog_titles SET title = '$content' WHERE title = '$pre'");
if(!$sql) {
echo "<p class='error'>Unable to update the title!</p>";
}
?>
You’ll need to create an include file that contains the connection information for your database (grab a sample include file here).
Update the path to your include file, enter your database connection information, save all three files, upload them, and refresh your rendered template. Make some changes to the titles. Refresh the page again, and your changes should remain.
Congratulations – you’ve just made a direct change to your EE database with AJAX!
Going a step further – editing more than just titles
You probably noticed in the demo that the excerpt for each post was also editable. Here’s how you do it:
Step 9: Create another editable element
First, you’ll need to tell the script that you want to make another set of IDs editable. Modify the init() function in editinplace.js to look like this:
function init() {
var max = 50;
for (var i = 1; i <= max; i++) {
makeEditable('editTitle_' + i);
makeEditable('editBody_' + i);
}
}
Next, modify your template by adding the new ID:
<p id="editBody_{count}">{body}</p>
Finally, to post any changes to your paragraphs to the database, you’ll need to modify edit.php:
<?php
$id = $_POST['id'];
$content = $_POST['content'];
$pre = $_POST['pre'];
echo htmlspecialchars($content);
require_once("path-to-your/include.inc.php");
if(stristr($id, 'Title') == true) {
$sql = mysql_query("UPDATE exp_weblog_titles SET title = '$content' WHERE title = '$pre'");
} elseif(stristr($id, 'Body') == true) {
$sql = mysql_query("UPDATE exp_weblog_data SET field_id_65 = '$content' WHERE field_id_65 = '$pre'");
}
?>
Since the PHP script is handling requests for both editTitle and editBody, we need to use the stristr() function to determine which ID is being passed in.
Be sure that the field ID matches your custom field! In the example above, it is 65, but yours is likely different.
You can find your field ID by going into the CP, clicking “Admin,” selecting “Custom Weblog Fields,” and hovering your mouse over the field name on the left. The field ID will appear at the very end of the URL string at the bottom left of your browser.
Step 10: Provide a textarea for paragraphs and a text input for titles
You probably don’t want to be editing large hunks of text in a tiny input box. To get a textarea to show up when you click on a paragraph (while keeping a text input for your titles), modify the edit() function in editinplace.js:
function edit(obj) {
Element.hide(obj);
if(obj.id.search(/Title/) != -1) {
// show an input box if we're editing a header
var textbox ='<div class="edit-input" id="' + obj.id + '_editor"><input name="' + obj.id + '" type="text" id="' + obj.id + '_edit" value="' + obj.innerHTML + '" />';
} else {
// else show a textarea
var textbox ='<div class="edit-area" id="' + obj.id + '_editor"><textarea name="' + obj.id + '" id="' + obj.id + '_edit">' + obj.innerHTML + '"</textarea>';
}
var button = '<input type="button" value="SAVE" id="' + obj.id + '_save"/> OR <input type="button" value="CANCEL" id="' + obj.id + '_cancel"/></div>';
new Insertion.After(obj, textbox+button);
Event.observe(obj.id+'_save', 'click', function(){saveChanges(obj)}, false);
Event.observe(obj.id+'_cancel', 'click', function(){cleanUp(obj)}, false);
}
Step 11: Limiting edits to certain member groups
Before you decide to go crazy with inline editing, remember that, without the proper checks, anyone who visits your site can make changes to your content.
For starters, you should wrap your editable areas in a {if logged_in} conditional. Once that’s done, you should also consider only allowing certain member groups to edit content inline. Here’s a quick fix:
<h2{if member_group == 1} id="editTitle_{count}"{/if}>{title}</h2>
These conditionals allow you to specify who can edit which areas. You could, for example, allow members to edit your body text, but only allow admins to edit your titles.
Limitations
While working through this script, I came up against a few roadblocks:
- When making more than one element (e.g., headers and paragraphs) editable, you must have an equal number of each. In other words, if there are 3 headers on the page that you want to edit, you can’t have more than 3 paragraph blocks that are editable. Paragraphs beyond the 3rd won’t be clickable.
- You have to pre-define a max number of editable fields (in my example, this is 50).
- Inline editing doesn’t work well with headings that are also anchors
Conclusion
This is my first attempt at getting inline editing to work with ExpressionEngine. I don’t profess to be a code guru, so it is my hope that a little bit of discussion can help improve the code here and make life easier for developers and end users everywhere.
I welcome your feedback, frustrations, and success stories. Good luck!

9 Comments
Nice write up Ryan! I’ve been looking to do inline editing with EE for a while. I haven’t had time to dig into your method yet, but it looks sound. I also like how you used the conditionals, nice touch. Maybe tomorrow I can implement this method on my site and see how it fairs. I shoot you an email one I get it all sorted out.
Ryan,
Thank you for this fine tutorial and innovative technique. I want to use it with my current project. This tutorial utilizes Text and Textarea fields only. Have you tried it using any other field types? I believe (from my limited research) that Prototype also supports Drop-Down fields as well. Your thoughts? This approach would be even more awesome if more field types were supported, like Jeditable and various Jquery plugins provide – http://www.appelsiini.net/projects/jeditable . If you wanted to develop something along these lines with your existing script(s), I would be happy to assist.
@James,
A bit late in coming, but my thanks to you!
@ Shane,
Let me see if I can come up with something that will allow drop down editing. I can definitely see the benefit. I’ve been in “design mode” lately, so I’ll have to get my code monkey hat back on.
I’ll post my findings here.
If you find anything related to dropdown/select menus and inline AJAX, let me know!
@Shane (and others),
I just posted a new entry on how to use a ModalBox to edit content inline. This newer method allows you to edit dropdown menus.
Tried the demo but getting a 404 error?
Deron,
The demo got lost in some deep abyss on my hard drive; I’ve taken it down for now.
There are many interesting I like this. Need to go now and add in RSS
I found what I wanna. It`s was great. Auther is the best
Fantastic stuff Ryan.
Post a Comment