getElementById vs. querySelector

JavaScript performance comparison

Revision 210 of this test case created by hight3ch

Preparation code

<p id="foo">Foo</p>
<p id="bar">Bar</p>
<p id="baz">Baz</p>
<script>
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

<p id="foo">Foo</p> <p id="bar">Bar</p> <p id="baz">Baz</p>

Test runner

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

Java applet disabled.

Testing in CCBot 2.0.0 / Other 0.0.0
Test Ops/sec
getElementById
var bar = document.getElementById("bar");
pending…
querySelector
var bar = $("#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.

0 Comments

Foo

Bar

Baz