QuerySpeed.js
(function(){ if(!String.prototype.trim){ String.prototype.trim = function(){ return this.replace(/^[\s\xa0]+|[\s\xa0]+$/g, ''); } } var mix = function(target, source ,override) { var i, ride = (override === void 0) || override; for (i in source) { if (ride || !(i in target)) { target[i] = source[i]; } } return target; } var dom = {}; var metaObject = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '\\': '\\\\' }, _startOfHTML = "\t__views.push(", _endOfHTML = ");\n", _rAt = /(^|[^\w\u00c0-\uFFFF_])(@)(?=\w)/g; (function(w,s){ //http://blogs.msdn.com/xmlteam/archive/2006/10/23/using-the-right-version-of-msxml-in-internet-explorer.aspx s = ["XMLHttpRequest", "ActiveXObject('Msxml2.XMLHTTP.6.0')", "ActiveXObject('Msxml2.XMLHTTP.3.0')", "ActiveXObject('Msxml2.XMLHTTP')", "ActiveXObject('Microsoft.XMLHTTP')"]; if( !-[1,] && w.ScriptEngineMinorVersion() === 7 && location.protocol === "file:"){ s.shift(); } for(var i = 0 ,el;el=s[i++];){ try{ if(eval("new "+el)){ dom.xhr = new Function( "return new "+el) break; } }catch(e){} } })(this); mix(dom,{ queryId : function (id, context) { var el = (context || document).getElementById(id); return el && [el] || [] }, queryTag : function(tag,context){ var flag_skip = tag !== "*",result = [],els = (context || document).getElementsByTagName(tag); if(-[1,]){ return Array.prototype.slice.call(els) }else{ for(var i = 0,ri = 0,el;el = els[i++];) if(flag_skip || el.nodeType === 1){ result[ri++] = el } } return result; }, each: function(array,fn,bind){ for (var i = 0, n = array.length; i < n; i++){ if(i in array){ if(fn.call(bind, array[i], i, array) === false){ break; } } } }, numberOfWindow : 0, numberOfIframe : 0, hasReady : function(){ if(dom.numberOfWindow === dom.numberOfIframe ){ var el = dom.queryId("hasReady")[0] el.innerHTML = "已经准备就绪!"; el.style.color = "#a9ea00"; } }, addEvent: document.addEventListener ? function(el,type,fn){ el.addEventListener(type,fn,false); } : function(el,type,fn){ el.attachEvent("on"+type,function(){ return fn.call(el,window.event) }) }, quote: String.quote || function (str) { return '"' + str.replace(/[\\\"\x00-\x1f]/g, function (a) { var c = metaObject[a]; return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); }) + '"' }, ejs_partial: function(url){ var xhr = dom.xhr(); xhr.open("GET",url,false); xhr.setRequestHeader("If-Modified-Since","0"); xhr.send(null); return xhr.responseText|| "" }, ejs: function(obj,usePart){ if(!usePart){//如果普通模板,则要整理配置文件 obj.left = obj.left || "<&"; obj.right = obj.right || "&>"; obj.rLeft = obj.rLeft || new RegExp("\\s*"+ obj.left+"\\s*"); obj.rRight = obj.rRight || new RegExp("\\s*"+ obj.right+"\\s*"); } //取得缓存索引 var key = obj.selector || obj.url, rAt = _rAt, startOfHTML = _startOfHTML, endOfHTML = _endOfHTML, self = arguments.callee, buff = ["var __views = [];\n"],str; if(obj.selector){ var el = dom.queryId(key); if (!el.length) throw "can not find the target element"; str = el[0].text; }else { str = dom.ejs_partial(obj.url); if (!str) throw "the target file does not exist"; } if(!self[key]){ var arr = str.trim().split(obj.rLeft),temp = [],url,selector,i=0,n=arr.length,els,segment,logic; while(i<n){ segment = arr[i++]; els = segment.split(obj.rRight); if(segment.indexOf(obj.right) !== -1){//这里不使用el.length === 2是为了避开IE的split bug switch (els[0].charAt(0)) { case "~"://include 局部模板 以URL方式引入 delete obj.selector; obj.url = els[0].substring(1).trim(); self[url] = self[url] || self(obj,true); temp = temp.concat(self[url] ); break; case ":"://include 局部模板 以元素选择符方式引入 delete obj.url; selector = obj.selector = els[0].substring(1).trim(); self[selector] = self[selector] || self(obj,true); alert(temp) temp = temp.concat(self[selector] ); break; case "="://处理后台返回的变量(输出到页面的); logic = els[0].substring(1); if(logic.indexOf("@") !== -1){ temp.push(startOfHTML, logic.replace(rAt,"$1data."), endOfHTML); }else{ temp.push(startOfHTML, logic, endOfHTML); } break; case "#"://处理注释 break; default: logic = els[0]; if(logic.indexOf("@") !== -1){ temp.push(logic.replace(rAt,"$1data."), "\n"); }else{ temp.push(logic, "\n"); } } els[1] && temp.push(startOfHTML, dom.quote.call(null,els[1]), endOfHTML) }else{ temp.push(startOfHTML, dom.quote.call(null,els[0]), endOfHTML) } } if(usePart){//局部模板 return (self[key] = temp); }else{ //缓存模板函数 self[key] = new Function("data", buff.concat(temp).join("") + ';return __views.join("");'); } } return self[key](obj.data || {}); } }); window.dom = dom; //要测试的框架 var frameworks = { "Peppy 0.1":{ disable:1, file:"peppy.js", fun:"$" }, "uuQuery v1.01":{ file:"uuQuery.js", fun:"uuQuery" }, "DOMAssistant2.8":{ disable:1, file:"DOMAssistantComplete-2.8.js", fun:"$" }, "query v3.9":{ file:"queryv39.js", fun:"dom.query" }, "Fox":{ disable:1, file:"Fox.js", fun:"Fox.query" }, "jQuery1.4.4":{ disable:1, file:"jquery1.4.4.js", fun:"jQuery" }, "Sizzle":{ file:"sizzle.js", fun:"Sizzle" }, "Sly v1.0rc2":{ disable:1, file:"Sly.js", fun:"Sly.search" }, "inQuery":{ disable:1, file:"inCore.js,inQuery.js", fun:"$" }, "EXT 2.2":{ disable:1, file:"ext-2.2.js", fun:"Ext.query" }, "JQuery 1.2.6":{ disable:1, file:"jquery-1.2.6.js", fun:"jQuery" }, "MooTools 1.3":{ disable:1, file:"mootools.js", fun:"$$" }, "YUI 2.6.0":{ disable:1, file:"YUI-2.6.0.selector-beta.js", fun:"YAHOO.util.Selector.query" }, "myframework":{ disable:1, file:"myFramework.js", fun:"getElements" }, "Dojo 1.2.0":{ disable:1, file:"dojo-1.2.0.js", fun:"dojo.query" } } //要测试的选择器 var selectors = [ "div :only-of-type", "th:first-of-type", "th:last-of-type", "td:nth-of-type(even)", "td:nth-last-of-type(odd)", "body :empty", "div:not(.example)", "p:contains(selectors)", "div p a", // "div, p, a", "div p", "body div", ".note", "ul.toc li.tocline2", "ul.toc > li.tocline2", "tr .pattern", "p", "div", "div ~ p", "div > div", "div ~ div", "div > p", "body", "div + p", "div ~ p", "div[class^=exa][class$=mple]", "div.example", "ul .tocline2", "div.example, div.note", "#title", "h1#title", "div #title", "h1#title + div > p", "a[href][lang][class]", "div[class]", "div[class=example]", "div[class^=exa]", "div[class$=mple]", "div[class*=e]", "div[class|=dialog]", "div[class!=made_up]", "div[class~=example]", "p:nth-child(even)", "p:nth-child(2n)", "p:nth-child(odd)", "p:nth-child(2n+1)", "p:nth-child(n)", "p:only-child", "p:last-child", "p:first-child", "div :only-child", "td:odd", "p:even" ] window.onload = function(){ dom.queryId("iframes_tc")[0].innerHTML = dom.ejs({ selector:"iframes_tmpl", data:{ frameworks:frameworks } }); dom.queryId("table_tc")[0].innerHTML = dom.ejs({ selector:"table_tmpl", data:{ frameworks:frameworks, selectors:selectors } }); var tbody = dom.queryTag('tbody')[0]; var tfoot = dom.queryTag('tfoot')[0]; var lastrow = dom.queryTag('tr',tfoot)[0]; var startTest = dom.queryId("startTest")[0]; var stopTest = dom.queryId("stopTest")[0]; var scoreNum= {}; //用于放置积分 var scoreTds = {};//用于放置存在积分的TD元素 var cellIndex = 1; var framework; for(var name in frameworks){ framework = frameworks[name]; if (!framework.disable){ framework.window = window.frames[name]; framework.selectors = selectors.concat(); scoreTds[name] = lastrow.cells[cellIndex++] dom.numberOfWindow++; scoreNum[name] = 0; } } var tests = [] ,rows = tbody.rows dom.each(selectors, function(selector, i){//构建测试单元数组,个数为tbody的TD数 var cellIndex = 1,row = rows[i],framework; for (var name in frameworks){ framework = frameworks[name]; if (!framework.disable){ tests.push({ 'window': framework.window,//执行环境 'selector': framework.selectors[i], 'name': name, 'row': row, 'cell' : row.cells[cellIndex++] }); } } }); var timeoutId; var testRunner = function(){ var test = tests.shift();//取得测试单元 if (!test || !test.window.test) return;//如果不存在选择器函数返回 var results = test.window.test(test.selector); test.cell.className = 'test'; test.cell.innerHTML = results.time + ' ms | ' + results.found + ' found';//花了多少时间与发现了多少个节点 test.cell.speed = results.time; if (results.error){ test.cell.innerHTML = results.time + ' ms | <span class="exception" title="' + results.error + '">error returned</a>'; test.cell.className += ' exception'; test.cell.found = 0; test.cell.error = true; } else { test.cell.found = results.found; test.cell.error = false; } scoreNum[test.name] += test.cell.speed; scoreTds[test.name].innerHTML = ' ' + scoreNum[test.name] + ' '; if (test.cell == test.row.lastChild) colourRow(test.row); timeoutId = setTimeout(testRunner, 300); }; //为Tbody的行着色 var colourRow = function(row){ var cells = row.cells var speeds = [];//用于存在速度 dom.each(cells,function(cell,i){ speeds[i] = cell.error ? 9999999999 : ~~cell.speed; }); var min = Math.min.apply({}, speeds); var max = Math.max.apply({}, speeds); var consistency = true; var found = []; dom.each(cells, function(cell, i){ if (!cell.error && "found" in cell ){ found.push(cell.found); var length = found.length; if(length > 2 && found[length-2] !== found[1-1] ){ return (consistency = false); } } if (cell.speed == min) cell.className += ' good'; else if (cell.speed == max) cell.className += ' bad'; else cell.className += ' normal'; }); if (!consistency){//如果各个框架取得的结果不一致,则全部 dom.each(cells, function(cell){ if (cell.found != undefined) cell.className += ' mismatch'; }); } }; dom.addEvent(startTest,"click",testRunner); dom.addEvent(stopTest,"click",function(){ clearTimeout(timeoutId); timeoutId = null; }); } }).call(this);
index.html
QuerySpeed~选择器测试速工具
template.html的javascript部分
var params = document.location.search.slice(1).split('&'), i = 0,pair, queryFnStr while((pair = params[i++])){ if(pair.indexOf("include")!==-1){ document.write('<script type="text/javascript" src="frameworks/'+pair.replace("include=","")+'"></sc'+'ript>') } if(pair.indexOf("function")!==-1){ queryFnStr = pair.replace("function=","") } } window.test = function(selector){ try { var start = new Date() - 0 var queryFn = arguments.callee.queryFn; var nodes = queryFn(selector); var time = new Date - start; return {'time': time, 'found': nodes.length}; } catch(err){ return {'time': time||1, 'found': -1, 'error': err}; } }; (function(){ try{ var queryFn = eval("window."+queryFnStr); // window.console.log(queryFnStr+"准备好了") queryFn("body") test.queryFn = queryFn; }catch(e){ setTimeout(arguments.callee,64) } })(); window.onload = function(){ window.parent.dom.numberOfIframe++; window.parent.dom.hasReady(); }