Firefox & Safari changed Event bubbling handling

Tags:

One of my first Web2.0 projects was to modify ZenPhoto into an online transcription for our spelling project. It uses editable DIV extensively, which means a lot of onclick events inside of onclick events. It was written back in 2005, and has been working well with Firefox 1.x, 2.x, and IE all versions.

Firefox 3.0RC1 and Safari 3.xb broke the code. The way it should work: once you clicked on an editable DIV, the javascript hides the DIV, adds a textarea, and "save" and "cancel" buttons. And then you click "save", it take new values in the textarea and replace whatever is in the hidden DIV. Then it unhide the DIV, get rid of the extra, and clear the flag "editing" — which prevends multiple editable DIVs.

Notice that there are 2 "onclick", one inside of the other — first onlick on the DIV, and then onclick the SAVE button. In the older browsers the onclick on SAVE does not bubble up to the DIV.

With the 3.x of Firefox and Safari, however, it looks like the event now bubbles up. The symptom is that you click on SAVE, it does whatever other things you asks it to do in the javascript function, but refuses to hide the SAVE button and unhide the DIV. So once a DIV is being clicked, you can’t close it or edit other DIVs.

So I did some debugging, inserting "alert()" all over. It turns out — see the messy code below — that as soon as I set the "editing=false", it does close the DIV, but when the function returns from "SAVE.onclick", the DIV is opened again.

My suspecion was that somehow the onclick did not stop and bubbled up. See Javascript - Event order. Adding code that stops the bubbling indeed solves the problem. And it appears it’s still compatible with Firefox 2.x and IE.

here’s the offensive code and my hack. So far I haven’t googled any mentioning of this on the web.

 

        editableAreas[i].onclick = function () {
            //this.onclick = null;

            if (editing == false) {
                editing = true;
                this.className = "editableField";
                var contentNow = this.innerHTML;
                this.innerHTML = "";
                
                // get book id (id is in page of type bookxxx where xx is the id in the database, so we get rid of the "book" part)
                //var idToEdit = this.parentNode.parentNode.getAttribute(’id’).substring(4);
                var idToEdit = this.getAttribute(’id’).substring(5);
               
                //$(’jsReturn’).innerHTML = "Editing field "+idToEdit+"…";
               
                var theForm = document.createElement(’form’);
                var theTextarea = document.createElement(’textarea’);
                var br = document.createElement(’br’);
                var theDelButton = document.createElement(’input’);
                var theSaveButton = document.createElement(’input’);
                var theStatus = document.createElement(’span’);
               
                theTextarea.setAttribute(’name’, ‘edit’+idToEdit);
                theTextarea.setAttribute(’cols’, 5);
                theTextarea.setAttribute(’rows’, 4);
                theTextarea.setAttribute(’action’, "edit.php?id="+idToEdit);
                theTextarea.setAttribute(’id’, "theField");
                theTextarea.innerHTML = contentNow;
                theTextarea.className = "editField";
               
                theDelButton.setAttribute(’name’, ‘cancel’);
                theDelButton.setAttribute(’type’, ‘button’);
                theDelButton.setAttribute(’id’, ‘cancelEdit’);
                theDelButton.setAttribute(’value’, ‘Cancel’);
                theDelButton.className = "delBut";

                theSaveButton.setAttribute(’name’, ’save’);
                theSaveButton.setAttribute(’type’, ‘button’);
                theSaveButton.setAttribute(’id’, ’saveEdit’);
                theSaveButton.setAttribute(’value’, ‘Save’);
                theSaveButton.className = "saveBut";
               
                theStatus.className = "ajaxStatus";
               
                this.appendChild(theForm);
                theForm.appendChild(theTextarea);
                theForm.appendChild(br);
                theForm.appendChild(theDelButton);
                theForm.appendChild(theSaveButton);
                theForm.appendChild(theStatus);
               
                theSaveButton.onclick = function (e) {
                    //$(’jsReturn’).innerHTML = "Saving field "+idToEdit+"!";
    if (!e) var e = window.event;
    e.cancelBubble = true;
    if (e.stopPropagation) e.stopPropagation();

                    //alert("Saving field "+idToEdit+"!\n"+this.parentNode.parentNode.id);
                    var fieldName = this.parentNode.parentNode.id;
                    this.parentNode.parentNode.innerHTML = $(’theField’).value;
                    // gary feng: do tokenizing
                    tokenize(fieldName);
                    editing = false;
                    //alert("Editing= "+editing);
                    //this.onclick = null;
                    return false;
                }
               
                theDelButton.onclick = function (e) {
                    //$(’jsReturn’).innerHTML = "Reset editing of field "+idToEdit+".";
    if (!e) var e = window.event;
    e.cancelBubble = true;
    if (e.stopPropagation) e.stopPropagation();

                    this.parentNode.parentNode.innerHTML = contentNow;
                    editing = false;
                    return false;
                }
            }
            return false;
        }
    }

Leave a Reply

If the above Image does not contain text, use this secure code: 9drph8