Question

I wrote a big function to update content on my website using Ajax Request, but the function which works with responseText freezes for some ms my page.

Can someone give me a hint on why this happens?

Here is my function :

//process Ajax responseText
window.processResponse = function(response) {
    if (!response) return;
    var div = document.createElement("div");
    div.innerHTML = response;
    var divElements = div.getElementsByTagName("*");
    var divChildren = div.children;
    //set unique id for each element and save all ids in array
    var ids = [];
    for (var i = 0; i < divElements.length; i++) {
        var em = divElements[i];
        if (!em.id) {
            var hash = (em.innerHTML || em.outerHTML).toHash();
            var o = 0;
            if (ids.inArray(hash)) em.id = hash + "_" + (++o);
            else em.id = hash;
        }
        ids.push(em.id);
    } //endfor
    for (var i = 0; i < divChildren.length; i++) {
        var root = divChildren[i];
        var documentRoot = document.getElementById(root.id);
        if (documentRoot) {
            if (documentRoot.getAttribute("page") != root.getAttribute("page")) {
                documentRoot.innerHTML = root.innerHTML;
                documentRoot.setAttribute("page", root.getAttribute("page"));
                return;
            }
            var pageHash = div.innerHTML.toHash();
            if (documentRoot.getAttribute("hash") == pageHash) {
                return;
            }
            documentRoot.setAttribute("hash", pageHash);
            var rootElements = root.getElementsByTagName("*");
            var node = null;
            var prevNode = {};
            var parentNode = null;
            var documentNode = null;
            var documentParentNode = null;
            var index = 0;
            var noChange = {};
            while (node = rootElements[index++]) {
                parentNode = node.parentNode;
                if (noChange[parentNode.id]) {
                    continue;
                }
                documentNode = document.getElementById(node.id);
                documentParentNode = document.getElementById(parentNode.id);
                if (!documentNode) {
                    //if element not exists then create new node
                    if (prevNode[parentNode.id]) documentParentNode.insertBefore(node, prevNode[parentNode.id].nextSibling);
                    else documentParentNode.insertBefore(node, documentParentNode.firstChild);
                    documentNode = node;
                    index--;
                } else {
                    //if node exists then check it's content
                    if (!node.children[0] && !documentNode.children[0]) {
                        var nodeHash = (node.innerHTML || node.outerHTML).toHash();
                        var documentHash = (documentNode.innerHTML || documentNode.outerHTML).toHash();
                        if (nodeHash != documentHash) {
                            documentNode.parentNode.insertBefore(node, documentNode.nextSibling);
                            documentNode.parentNode.removeChild(documentNode);
                            documentNode = node;
                            index--;
                        }
                    }
                    //if loaded node has no children then just replace old node with new one
                    else if (!node.children[0]) {
                        documentNode.parentNode.insertBefore(node, documentNode.nextSibling);
                        documentNode.parentNode.removeChild(documentNode);
                        documentNode = node;
                        index--;
                    } else {
                        var nodeHash = (node.innerHTML || node.outerHTML).toHash();
                        var documentHash = (documentNode.innerHTML || documentNode.outerHTML).toHash();
                        if (nodeHash == documentHash) noChange[node.id] = true;
                    }
                }
                //save previous node
                prevNode[parentNode.id] = documentNode;
            } //endwhile
            //remove unneded nodes
            var documentRootElements = documentRoot.getElementsByTagName("*");
            for (var j = 0; j < documentRootElements.length; j++) {
                if (!ids.inArray(documentRootElements[j].id)) {
                    documentRootElements[j].parentNode.removeChild(documentRootElements[j]);
                    j--;
                }
            } //endfor
        }
    } //endfor
};
//generate hashCode from string
String.prototype.toHash = function() {
    var hash = 0,
        i, chr;
    var str = this.clear();
    if (str.length == 0) return hash;
    for (i = 0; i < str.length; i++) {
        chr = str.charCodeAt(i);
        hash = ((hash << 5) - hash) + chr;
        hash = hash & hash;
    }
    return hash.toString();
};
Was it helpful?

Solution

Okay, so first problem is that you've got this huge function that is constantly grabbing all elements, and then re-grabbing the same nodes at each level of iteration.

At least you're not appending the div to the DOM, before you do so -- that would be JS-suicide.

Why not a depth-first traversal, with recursion, which would at least save the time of grabbing all of the child elements, multiple times (unless you're sure that's going to demolish your stack, in which case, there's little you can do to make it one speedy function)?

Also: JS is single-threaded. If you're chewing through all of this in one go, your UI is going to be completely unresponsive until you make it all the way through.

Going the recursive route, if you broke this function out into multiple steps would also allow you to get your UI back.

If you can add even a simple 10ms setTimeout(recurseHTML, 10); in there (or an anonymous function which wraps whatever parameters you'll want to send in), that will give the browser time to complete any interaction the user has been waiting to perform.

It means that you'll have to think a little harder about how your "finished" conditions are met, and it means that you'll have to wait a while longer for the process to finish -- but usability trumps both of those things.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top