jsPerf.app is an online JavaScript performance benchmark test runner & jsperf.com mirror. It is a complete rewrite in homage to the once excellent jsperf.com now with hopefully a more modern & maintainable codebase.
jsperf.com URLs are mirrored at the same path, e.g:
https://jsperf.com/negative-modulo/2
Can be accessed at:
https://jsperf.app/negative-modulo/2
When querying a complex selector (multiple tag names and attributes), compare the use of an intermediary query() method to manually calling getElementsByTagName and getElementsByClassName separately.
<style>
div.a, div.b, p.a, p[id^="result"] {
border: 1px solid #444;
padding: 3px;
}
</style>
<div class="a">div.a
<div class="a b">div.a.b
<input type="text" class="a" value="input.a">
<input type="button" class="a" value="input.a">
<input type="button" class="b" value="input.b">
<p class="b">p.b</p>
</div>
</div>
<div class="b">div.b
<div>div
<input type="text" value="input">
<input type="button" value="input">
<input type="button" value="input">
<input type="button" class="a b" value="input.a.b">
</div>
<div class="b">div.b</div>
</div>
<p id="result1">p</p>
<p id="result2">p</p>
<p id="result3">p</p>
<p id="result4">p</p>
<p id="result5">p</p>
<p id="result6">p</p>
var elements, i, results, filter1;
function query (sSelector,oNode,oSettings)
{
if (!sSelector || typeof sSelector !== 'string') { return false; }
var aElements = [], sTagName, sProperty, i,
bIsArray = false,
aSelectors = sSelector.replace(/\s*\,\s*/,',').split(','),
bSingleSelector = (aSelectors.length === 1);
oNode = oNode || document;
// Check settings
oSettings = oSettings || {filter:null,toArray:false};
fFilter = oSettings.filter || null;
bToArray = oSettings.toArray || false;
// Loop through each selector
i = 0;
while (i < aSelectors.length) {
sSelector = aSelectors[i];
// By class name
if (sSelector.indexOf('.') > -1) {
sTagName = sSelector.split('.')[0] || "*";
sProperty = sSelector.split('.')[1] || "";
// If there is only one selector or the previous selectors did not return
// anything, this will be the only set of results so far
if (bSingleSelector || !aElements.length) {
aElements = getElementsByClass(oNode,sTagName,sProperty);
}
else {
// Otherwise, these results will need to be concatenated with the others
// so we need an actual array
if (!bIsArray && aElements.length) {
aElements = toArray(aElements);
}
aElements = aElements.concat(getElementsByClass(oNode,sTagName,sProperty));
}
bIsArray = true; // getElementsByClass() always returns an array
}
// By element ID
else if (sSelector.indexOf('#') > -1) {
// Get the ID
sProperty = sSelector.substr(sSelector.indexOf('#')+1);
if (oNode.getElementById(sProperty)) {
// If the results list is not already an array and it's not empty, it must be converted first
if (!bSingleSelector && !bIsArray && aElements.length) {
aElements = toArray(aElements);
}
aElements.push(oNode.getElementById(sProperty));
bIsArray = true;
}
}
// By tag name
else {
// Don't need to convert to an array if these are the only results so far
if (bSingleSelector || !aElements.length) {
aElements = oNode.getElementsByTagName(sSelector);
}
else {
if (!bIsArray && aElements.length) {
aElements = toArray(aElements);
bIsArray = true;
}
aElements = aElements.concat(toArray(oNode.getElementsByTagName(sSelector)));
}
}
i++;
}
// Convert to array based on settings, if it hasn't been done already
if (!bIsArray && (bToArray || fFilter)) {
aElements = toArray(aElements);
bIsArray = true;
}
// Filter results
if (fFilter) {
i = 0;
while (i < aElements.length) {
if (!fFilter(aElements[i])) {
// Remove this element from the array
aElements.splice(i, 1);
i--;
}
i++;
}
}
return aElements;
}
function toArray (aList) {
var aArray = [];
try {
// This method is defined in IE7/8 but doesn't work on all types of lists
aArray = Array.prototype.slice.call(aList);
} catch (e) { }
if (!aArray.length && aList.length) {
var i = 0;
while (i < aList.length) {
aArray.push(aList[i]);
i++;
}
}
return aArray;
}
function getElementsByClass (oNode, sTag, sClassNames)
{
if (!sClassNames || typeof sClassNames != 'string') { return false; }
// Set defaults if these parameters weren't specified
if (typeof oNode != 'object') { oNode = document; }
if (!sTag) { sTag = '*'; }
var i, j, aReturnElements = []
// Trim class list, reduce whitespace to single spaces, then split on spaces
,aClasses = sClassNames.replace(/^\s+|\s+$/g,'').replace(/\s+/g,' ').split(' ')
// Get all elements to search through
,aElements = oNode.getElementsByTagName(sTag);
// Loop through elements and check each one's classes against the list
// The outer loop cannot be 'reversed' like while(i--), causes problems with clickCheckbox()
i = 0;
while (i < aElements.length) {
j = aClasses.length;
while (j--) {
if (hasClass(aElements[i],aClasses[j])) {
aReturnElements.push(aElements[i]);
// Quit the classes loop so this element doesn't get added again if it matches another class
break;
}
}
i++;
}
return aReturnElements;
}
function hasClass (oElem,sClassName)
{
// Fix oElem if 'this' was passed in IE
if (!oElem && window.event) { oElem = event.srcElement; }
// Check parameters
if (!oElem.className || typeof sClassName != 'string') { return null; }
// Test for the class
sClassName = ' ' + sClassName.replace(/^\s+|\s+$/g,'') + ' ';
return (' ' + oElem.className + ' ').indexOf(sClassName) > -1;
}
Benchmark.prototype.setup = function() {
elements = [];
i = 0;
results = [];
filter1 = null;
};
elements = [];
i = 0;
results = [];
filter1 = null;
Ready to run.
Test | Ops/sec | |
---|---|---|
Manual tag names |
| ready |
Query() tag names |
| ready |
Manual class names |
| ready |
Query() class names |
| ready |
Manual complex |
| ready |
Query() complex |
| ready |
You can edit these tests or add more tests to this page by appending /edit to the URL.