views:

867

answers:

1

I found a tutorial on how to create editable regions on a page using AJAX.

This is great, except it was written for a single element with a unique ID. I'd like to be able to click on multiple elements on the same page and have them also be editable (e.g., I'd like to alter the script below so it works not with a single element, but with multiple elements of a particular class).

Here is my HTML:

<h2>Edit This</h2>
<p class="edit">This is some editable content</p>
<p class="edit">This is some more editable content</p>
<p class="edit">I could do this all day</p>

Here is the JS file I'm working with (I updated the script per Rex's answer below): This script is, unfortunately, not working - can anyone point me in the right direction?

Event.observe(window, 'load', init, false);

function init() {
    makeEditable('edit');
}

function makeEditable(className) {
    var editElements = document.getElementsByClassName(className);
    for(var i=0;i<editElements.length;i++) {
        Event.observe(editElements[i], 'click', function(){edit($(className))}, false);
     Event.observe(editElements[i], 'mouseover', function(){showAsEditable($(className))}, false);
     Event.observe(editElements[i], 'mouseout', function(){showAsEditable($(className), true)}, false);
    }
}


function showAsEditable(obj, clear) {
    if (!clear) {
     Element.addClassName(obj, 'editable');
    } else {
     Element.removeClassName(obj, 'editable');
    }
}

function edit(obj) {
    Element.hide(obj);

    var textarea ='<div id="' + obj.id + '_editor"><textarea cols="60" rows="4" 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, textarea+button);

    Event.observe(obj.id+'_save', 'click', function(){saveChanges(obj)}, false);
    Event.observe(obj.id+'_cancel', 'click', function(){cleanUp(obj)}, false);
}

function cleanUp(obj, keepEditable) {
    Element.remove(obj.id+'_editor');
    Element.show(obj);
    if (!keepEditable) showAsEditable(obj, true);
}

function saveChanges(obj) {
    var new_content = escape($F(obj.id+'_edit'));

    obj.preUpdate = obj.innerHTML // stow contents prior to saving in case of an error
    obj.innerHTML = "Saving…";
    cleanUp(obj, true);

    var success = function(t){editComplete(t, obj);}
    var failure = function(t){editFailed(t, obj);}

    var url = 'http://portal.3roadsmedia.com/scripts/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});
}

function editComplete(t, obj) {
    obj.innerHTML = t.responseText;
    showAsEditable(obj, true);
}

function editFailed(t, obj) {
    obj.innerHTML = 'Sorry, the update failed.';
    cleanUp(obj);
}
+1  A: 

The Event.observe method currently attaches to a single element with the ID specified. You should change this to iterate over a collection of elements located by classname and attach to each of them. According to the Prototype documentation, you can provide an element object as the first parameter, instead of an ID.

Currently, id is a string:

function makeEditable(id) {
    Event.observe(id, 'click', function(){edit($(id))}, false);
    //...

Which means Event.observe is attaching to the click event of the element with the ID provided. You want to attach to all elements with a class. Try:

function makeEditable(className) {
    var editElements = document.getElementsByClassName(className);
    for(var i=0;i<editElements.length;i++) {
        Event.observe(editElements[i], 'click', function()
        //...
    }
    //...
Rex M
Rex,Thanks for the quick response. Would you be willing to expand on that a bit?
Ryan
@Ryan see updates in answer
Rex M
Rex, would I then need to replace all instances of obj.id with obj.className (I'm specifically looking at the edit() function).
Ryan
@Ryan no, the edit() function is invoked when you select a specific zone to be edited. So the contents of edit() should still only refer to the single (by ID) element currently being edited. You only need to use classes for wiring up the editing logic to the zones.
Rex M
Rex, thanks. I updated the code above to reflect these changes, keeping the obj.id in the other functions.Unfortunately, not even the showAsEditable function is getting called, since on mouseover/mouseout the CSS class remains unchanged. The function calls look correct...
Ryan
When you say "zones" I assume you mean each individual <p> of class "edit." Would these not still be classes when edited? I tried class="obj.id" and id="obj.className" and class="obj.className" but none of those combos worked.
Ryan
I discovered that Element.addClassName and Element.removeClassName require an ID to be passed in, so the way we're doing it won't work. I would have to assign each <p> an ID (something like id="para_1", "para_2", etc.), then call it like this within the same FOR loop: showAsEditable($(id+'_'+[i])).
Ryan