之前也曾经有过一系列《QWrap Selector解密》 文章,讲到过QWrap里的dom/selector.js里的大多数要点。今天又整了一个完全依赖浏览器的querySelectorAll的slector版本:selector_w3c.js。与之前的selector.js的接口名称相同。
我们自己写一个selector.js,这样我们就有弥补标准的不足的自由。例如,就算最新的浏览器,也依然没有人严格实现matchesSelector。而我们的selector.js里早就有了filter与test方法来实现matchesSelector的功能。
从另一个角度看,我们有时需要为某些特定场景定制一些代码,以些来节约网络流量。selector_w3c.js就是假设浏览器已支持querySelectorAll方法。典型的例子就是:基于移动终端做的web js开发。
selector_w3c.js与selector.js对外提供的所有方法名称都一样,只是有w3c版里对参数的要求与对应的处理,更依赖于浏览器。
例如:
- 差别1:原来的selector.js中的Selector.query(document.body,'body div')不会反回结果。因为它为了易用性的考虑,把后面的selector字符串默认加上了“:scope ”,相当于selector_w3c.js里的Selector.query(document.body,':scope body div'),尽管现在Chrome也没有实现:scope伪类。这种差别在这篇《认识selector写法》 的文章中也讲过,是一个严谨性与易用性冲突的典型例子。
- 差别2:seletor_w3c.js里的test方法里的selector参数现在也支持各种关系符了。例如,可以这样写:Selector.test(document.body,'html body')。同时它也产生了一个副作用,即:如果被测试的元素没有父元素(例如新建的、还未插入到dom树里去的节点),则测试结果为假。
- 差别3:seletor_w3c.js里的filter(els, sSelector, pEl)在未传pEl时,会忽略游离在dom树之外的节点。
很可惜,因为chrome也没有实现matchesSelector,所以test、filter两个方法我只能去山寨的去实现一下,所以有了上面的差别2与差别3里的瑕玼。----这说明,“标准”总走跑不赢我们的期望,现实的浏览器又跑不赢标准。----管他呢,反正那“标准”是“草案”还是“建议”还是其他的什么,都不是我能左右的。
代码具体代码在这里: https://github.com/wedteam/qwrap/blob/master/resource/js/dom/selector_w3c.js
其实很简单,如下:
/*
Copyright (c) Baidu Youa Wed QWrap
version: $version$ $release$ released
author: JK
*/
/**
* @class Selector Css Selector相关的几个方法
* @singleton
* @namespace QW
*/
(function() {
var Selector = {
/**
* 把一个selector字符串转化成一个过滤函数.
* @method selector2Filter
* @static
* @param {string} sSelector 过滤selector
* @returns {function} : 返回过滤函数。
* @example:
var fun=selector2Filter("input.aaa");alert(fun);
*/
selector2Filter: function(sSelector) {
return s2f(sSelector);
},
/**
* 判断一个元素是否符合某selector.
* @method test
* @static
* @param {HTMLElement} el: 被考察参数
* @param {string} sSelector: 过滤selector
* @returns {function} : 返回过滤函数。
*/
test: function(el, sSelector) {
return s2f(sSelector)(el);
},
/**
* 用一个css selector来过滤一个数组.
* @method filter
* @static
* @param {Array|Collection} els: 元素数组
* @param {string} sSelector: 过滤selector。
* @param {Element} pEl: 父节点。默认是document
* @returns {Array} : 返回满足过滤条件的元素组成的数组。
*/
filter: function(els, sSelector, pEl) {
var allEls = (pEl || document).querySelectorAll(sSelector);
return Array.filter(els, function(el){
return Array.indexOf(allEls,el) > -1;
});
},
/**
* 以refEl为参考,得到符合过滤条件的HTML Elements. refEl可以是element或者是document
* @method query
* @static
* @param {HTMLElement} refEl: 参考对象
* @param {string} sSelector: 过滤selector,
* @returns {array} : 返回elements数组。
* @example:
var els=query(document,"li input.aaa");
for(var i=0;i<els.length;i++ )els[i].style.backgroundColor='red';
*/
query: function(refEl, sSelector) {
return toArray((refEl || document).querySelectorAll(sSelector));
},
/**
* 以refEl为参考,得到符合过滤条件的一个元素. refEl可以是element或者是document
* @method one
* @static
* @param {HTMLElement} refEl: 参考对象
* @param {string} sSelector: 过滤selector,
* @returns {HTMLElement} : 返回element,如果获取不到,则反回null。
* @example:
var els=query(document,"li input.aaa");
for(var i=0;i<els.length;i++ )els[i].style.backgroundColor='red';
*/
one: function(refEl, sSelector) {
return (refEl || document).querySelector(sSelector);
}
};
/*
* s2f(sSelector): 由一个selector得到一个过滤函数filter
*/
var filterCache = {};
filterCache[''] = function(){
return true;
};
filterCache['*'] = function(el){
return !!el.tagName;
};
function s2f(sSelector) {
if (!filterCache[sSelector]) {
filterCache[sSelector] = function (el) {
return el.parentNode && Array.indexOf(el.parentNode.querySelectorAll(sSelector), el) > -1;
}
}
return filterCache[sSelector];
}
function toArray(arr){
for (var i=arr.length-1, ret = []; i>-1; i--) {
ret[i] = arr[i];
}
return ret;
}
QW.Selector = Selector;
}());
另:由于QWrap有灵活的apps机制,所以我们可以订制一个apps/qwrap_webkit.js的应用,用selector_w3c.js来取代selector.js。而其他地方的代码不用任何改变。
附:
QWrap博客网址:http://www.qwrap.com
Selector速度比赛:http://dev.qwrap.com/resource/js/_tools/speedmatch/_examples/SelectorSpeed.html