getElementById vs. querySelector

JavaScript performance comparison

Revision 45 of this test case created by Bigger dom

Info

Compare the speed of getElementById to querySelector

Preparation code

<p id="foo">Foo</p>
<p id="bar">Bar</p>
<p id="baz">Baz</p>
<script type="text/javascript">
function DOMHashMap(){

        this._tagMap = {};
        this._classMap = {};
        this._idMap = {};
       
        /*insert element into class and tag hash lists*/
        this.put = function(e){
                /*class*/
                if(e.className){
                        var classes = e.className.split(" ");
                        for(var i = 0; i < classes.length; i++){
                                if(typeof(this._classMap[classes[i]]) == 'undefined'){
                                        this._classMap[classes[i]] = [];
                                }
                                this._classMap[classes[i]].push(e);
                        }
                }
                /*id*/
                if(e.id){
                        this._idMap[e.id] = e;
                }
                /*tag*/
                if(typeof(this._tagMap[e.tagName]) == 'undefined'){
                        this._tagMap[e.tagName] = [];
                }
                this._tagMap[e.tagName].push(e);
        };
       
        /*return a hash list*/
        this.getClass = function(k){
                return this._classMap[k];
        };
       
        /*return a hash list*/
        this.getId = function(k){
                return this._idMap[k];
        };
       
        /*return a hash list*/
        this.getTag = function(k){
                return this._tagMap[k.toUpperCase()];
        };
       
        /*remove a document element from the hash map
        parameter e is a document element object*/

        this.remove = function(e){
                /*delete from class hash list*/
                if(e.className){
                        var classes = e.className.split(" ");
                        for(var i = 0; i < classes.length; i++){
                                if(typeof(this._classMap[classes[i]]) != 'undefined'){
                                        for(var j = 0; j <this._classMap[classes[i]].length; j++){
                                                if(e === this._classMap[classes[i]][j]) this._classMap[classes[i]].splice(j,1);
                                        }
                                }
                        }
                }
                /*delete from id hash list*/
                if(e.id){
                        var tag = e.id;
                        if(typeof(this._idMap[tag]) != 'undefined'){
                                for(var i = 0; i <this._idMap[tag].length; i++){
                                        if(e === this._idMap[tag][i]) this._idMap[tag].splice(i,1);
                                }
                        }
                }
                /*delete from tag hash list*/
                var tag = e.tagName;
                if(typeof(this._tagMap[tag]) != 'undefined'){
                        for(var i = 0; i <this._tagMap[tag].length; i++){
                                if(e === this._tagMap[tag][i]) this._tagMap[tag].splice(i,1);
                        }
                }
        };
}

/** updateChildHashMap : FUNCTION
*
*       @param : root(HTML DOM node) node that's children are to be populated/deleted with/of DOMHashMaps
*
*       @param : isAdd(boolean) whether to add or remove the sub children of root
*
*       @return : childList(array) of children of root(including root)
*
*       Walks through the subtree of @param root either initializing the DOMHashMaps of its children, or populating an array of the children to be removed from its ancestors -
*       depending on @param isAdd
*      
*/

function updateChildHashMap(root,isAdd){

        root.exists = 1;

        if(typeof(root.map) == 'undefined'){
                root.map = new DOMHashMap();
        }
        /*basecase*/
        if(typeof(root.children) == 'undefined') return new Array();
        else if(root.children.length == 0){
                var list = [];
                list.push(root);
                return list;
        }
        /*recursive step*/
        else{
                var childList = new Array();
                for(var i = 0; i < root.children.length; i++){
                                var currentChildList = {};
                                currentChildList.list = updateChildHashMap(root.children[i],isAdd);
                                for(var j = 0; j < currentChildList.list.length; j++){
                                        childList.push(currentChildList.list[j]);
                                        if(isAdd) root.map.put(currentChildList.list[j]);
                                        else root.map.remove(currentChildList.list[j]);
                                }
                                delete currentChildList.list;
                                currentChildList = null;
                }
                childList.push(root);
                return childList;
        }
}

/** updateAncestorHashMap : FUNCTION
*
*       @param : target(HTML DOM node) to add/remove a list of newly appended/removed children
*
*       @param : childList(array) of children to add/remove to/from @param target
*
*       @param : isAdd(boolean) whether to add or remove the childList
*
*       @return : None
*
*       Walks up the tree from the newly appended/removed node to remove or delete the children
*      
*/

function updateAncestorHashMap(target,childList,isAdd){
        if(target.parentNode){
                for(var i = 0; i < childList.length; i++){
                        if(isAdd) target.parentNode.map.put(childList[i]);
                        else target.parentNode.map.remove(childList[i]);
                }
                if(target.parentNode.tagName != "BODY") updateAncestorHashMap(target.parentNode,childList,isAdd);
                else return;
        }
        else return;
}


/** updateHashMap : FUNCTION
*
*       @param : target(HTML DOM node) to add/remove a list of newly appended/removed children
*
*       @param : isAdd(boolean) whether to add or remove the childList
*
*       @return : None
*
*       update the DOMHashMaps of all involved HTML Nodes
*      
*/

function updateHashMap(target,isAdd){
        var childList = {};
        childList.list = updateChildHashMap(target,isAdd);
        updateAncestorHashMap(target,childList.list,isAdd);
        delete childList.list;
        childList = null;
}


/** initialize : FUNCTION
*
*       @param : None
*
*       @return : None
*
*       Initailize DOMHashMaps for all current HTML DOM nodes in the current document.
*       Replace native appendChild and removeChild with new versions that update the DOMHashMaps.
*       Adds fastGetElement[s]By[Tag|Class|Id) to all the HTML DOM Element prototypes
*      
*/

function initialize(){

        /*indentify browser*/
        var browser = {
                chrome : false,
                minefield : false,
                firefox : false,
                ie : false,
        };
        if(navigator.userAgent.match("Chrome")) browser.chrome = true;
        else if(navigator.userAgent.match("Minefield")) browser.minefield = true;
        else if(navigator.userAgent.match("Mozilla")) browser.firefox = true;
       
        /*create new DOM manipulation functions to replace existing*/
        var getParent = function(e){
                if(e.parentNode){
                        if(e.parentNode.map) return e;
                        else return getParent(e.parentNode);
                }
                else return e;
        }
        var appendChildWithEvent = function(e){
                /*if e exists in the document, remove it first from the DOMHashMaps*/
                if(e.exists) updateHashMap(e,false);
                this.nativeAppendChild(e);
                e = getParent(e);
                updateHashMap(e,true);
        };
        var removeChildWithEvent = function(e){
                parent = getParent(e);
                updateHashMap(parent,false);
                this.nativeRemoveChild(e);
        };
       
        /*add DOM manipulation functions to the document*/
        document.haliaGetElementsByTagName = function(tag){return this.body.map.getTag(tag);};
        document.haliaGetElementsByClassName = function(tag){return this.body.map.getClass(tag);};
        document.haliaGetElementById = function(tag){return this.body.map.getId(tag);};
       
       
        /*browser specific application of new DOM manipulation functions*/
        if(browser.chrome){
                Element.prototype.nativeAppendChild = Element.prototype.appendChild;
                Element.prototype.appendChild = appendChildWithEvent;
                Element.prototype.nativeRemoveChild = Element.prototype.removeChild;
                Element.prototype.removeChild = removeChildWithEvent;
                Element.prototype.haliaGetElementsByTagName = function(tag){return this.map.getTag(tag);};
                Element.prototype.haliaGetElementsByClassName = function(tag){return this.map.getClass(tag);};
                Element.prototype.haliaGetElementById = function(tag){return this.map.getId(tag);};
        }
        else if(browser.minefield){
                var elements = ["HTMLHtmlElement","HTMLHeadElement","HTMLLinkElement","HTMLTitleElement","HTMLMetaElement",
                                                "HTMLBaseElement","HTMLIsIndexElement","HTMLStyleElement","HTMLBodyElement","HTMLFormElement",
                                                "HTMLSelectElement","HTMLOptGroupElement","HTMLOptionElement","HTMLInputElement","HTMLTextAreaElement",
                                                "HTMLButtonElement","HTMLLabelElement","HTMLFieldSetElement","HTMLLegendElement","HTMLUListElement",
                                                "HTMLOListElement","HTMLDListElement","HTMLDirectoryElement","HTMLMenuElement","HTMLLIElement",
                                                "HTMLDivElement","HTMLParagraphElement","HTMLHeadingElement","HTMLQuoteElement","HTMLPreElement",
                                                "HTMLFontElement","HTMLHRElement","HTMLAnchorElement",
                                                "HTMLImageElement","HTMLObjectElement","HTMLParamElement","HTMLAppletElement","HTMLMapElement",
                                                "HTMLAreaElement","HTMLScriptElement","HTMLTableElement","HTMLTableCaptionElement","HTMLTableColElement",
                                                "HTMLTableSectionElement","HTMLTableRowElement","HTMLTableCellElement","HTMLFrameSetElement","HTMLFrameElement",
                                                "HTMLIFrameElement"];
                                               
                for(var i = 0; i < elements.length; i++){
                        var e = eval(elements[i]);
                        e.prototype.nativeAppendChild = e.prototype.appendChild;
                        e.prototype.appendChild = appendChildWithEvent;
                        e.prototype.nativeRemoveChild = e.prototype.removeChild;
                        e.prototype.removeChild = removeChildWithEvent;
                        e.prototype.haliaGetElementsByTagName = function(tag){return this.map.getTag(tag);};
                        e.prototype.haliaGetElementsByClassName = function(tag){return this.map.getClass(tag);};
                        e.prototype.haliaGetElementById = function(tag){return this.map.getId(tag);};
                }
        }
        else if(browser.firefox){
                var elements = ["HTMLHtmlElement","HTMLHeadElement","HTMLLinkElement","HTMLTitleElement","HTMLMetaElement",
                                                "HTMLBaseElement","HTMLIsIndexElement","HTMLStyleElement","HTMLBodyElement","HTMLFormElement",
                                                "HTMLSelectElement","HTMLOptGroupElement","HTMLOptionElement","HTMLInputElement","HTMLTextAreaElement",
                                                "HTMLButtonElement","HTMLLabelElement","HTMLFieldSetElement","HTMLLegendElement","HTMLUListElement",
                                                "HTMLOListElement","HTMLDListElement","HTMLDirectoryElement","HTMLMenuElement","HTMLLIElement",
                                                "HTMLDivElement","HTMLParagraphElement","HTMLHeadingElement","HTMLQuoteElement","HTMLPreElement",
                                                "HTMLFontElement","HTMLHRElement","HTMLAnchorElement",
                                                "HTMLImageElement","HTMLObjectElement","HTMLParamElement","HTMLAppletElement","HTMLMapElement",
                                                "HTMLAreaElement","HTMLScriptElement","HTMLTableElement","HTMLTableCaptionElement","HTMLTableColElement",
                                                "HTMLTableSectionElement","HTMLTableRowElement","HTMLTableCellElement","HTMLFrameSetElement","HTMLFrameElement",
                                                "HTMLIFrameElement"];
                                               
                for(var i = 0; i < elements.length; i++){
                        var e = eval(elements[i]);
                        e.prototype.nativeAppendChild = e.prototype.appendChild;
                        e.prototype.appendChild = appendChildWithEvent;
                        e.prototype.nativeRemoveChild = e.prototype.removeChild;
                        e.prototype.removeChild = removeChildWithEvent;
                        e.prototype.haliaGetElementsByTagName = function(tag){return this.map.getTag(tag);};
                        e.prototype.haliaGetElementsByClassName = function(tag){return this.map.getClass(tag);};
                        e.prototype.haliaGetElementById = function(tag){return this.map.getId(tag);};
                }
        }
       
        /*initialize the DOMHashMaps for the elements in the current document*/
        updateChildHashMap(document.body,true);
}

initialize();
</script>

Preparation code output

Foo

Bar

Baz

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in unknown unknown
Test Ops/sec
getElementById
var bar = document.getElementById("bar");
pending…
querySelector
var bar = document.querySelector("#bar");
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL. Here’s a list of current revisions for this page:

0 comments

Add a comment