#
看到最近司徒同学发了好几篇与selector有关的文章。就也草写一篇有关selector的一些杂想。

以QWrap.Selector为例,它提供的几个方法。
query(refEl, sSelector)
one(refEl, sSelector)
filter(els, sSelector, pEl)
selector2Filter(sSelector)
test(el, sSelector)

代码
/**
* 以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){
//...
}

 


query(refEl,sSelector)是对refEl.querySelectorAll(sSelector)方法的模拟。对象方法变成静态方法,将对象变成静态方法的第一个参数。
市面上的selector,通常把refEl当第二个参数,如:Sizzle = function(selector, context, ...)。
可能是因为Sizzle考虑到易用性,所以倒装了一下。JK不赞成这种做法,觉得易用性应该是jquery考虑的,而不应该是由sizzle考虑。

先说明一下selector的语法。
A: 复杂selector
一个复杂的selector可以由“,”来分割成多个简单的selector。元素满足其中的一个简单selector,即通过筛选。
例如“div a, div input[type=button]”可以折成“div a”与“div input[type=button]”。
如果分开query,然后相关,那么理论上就会需要进行“并集除重”“排序”两个操作。
除重与排序会损失一些性能,需要权衡利弊(当然可以多添些代码来减小损失)。例如,QWrap放弃了在存在“,”对除重与排序的严谨性追求。

B: 简单selector
把没有“,”的selector当作简单selector。
例如:“div.aaa”“div a.aaa”“div>a.aaa”
css2+css3,一共有四种关系符:
“ ”----子孙
“>”----儿子
“+”----最大的弟弟
“~”----所有的弟弟
例如:一个var els=QW.Selector.query(document.body,"div>a")
它的意思是:在document.body下找,父亲是<div>的<a>元素。

C:最简单selector。
我们把没有关系符的selector叫最简单selector。
例如:
“input.aaa[readOnly]”
“input:nth-last-child(2n)”
“.aaa”


我们再看下简单“B: 简单selector”。
例如“div>a.aaa”
它是由两个最简selector中间夹一个关系符组成,这是我们直观的看到的情况。
为什么最简selector有两个,而关系符只有一个?
为什么?
因为它省略了第一个关系符“ ”
即,理论上,它应该这么写“ div>a.aaa”。
在css的selector中,可能很少有第一个关系符是非“ ”的情况,但是在js里,却是很常用的功能。
例如,QW里的用法:W('ul.aaa').delegate('>li','click',func)。它的意思是,为ul.aaa元素加上事件代理,代理只针对它们的直接儿子li,而不是针对它子子孙孙里所有的li。
----注:jquery对这种情况,写法似复要复杂一些。略过jquery的相关写法。

由于第一个关系符不一定是“ ”,所以,对于从右到左的selector实现方来式说,需要特别处理。
例如:<span onclick="alert('找我的弟弟:'+QW.Selector.query(this,'+span'));">点我</span><span>要找的人</span>

代码
/**
* 以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){
//...
}

 


one(refEl,sSelector)是对refEl.querySelector(sSelector)方法的模拟。对象方法变成静态方法,将对象变成静态方法的第一个参数。
获取得到一个满足sSelector的元素。
QW.Selector偷懒,直接用query(refEl,sSelector)[0]。这样会有很大的性能损失。以后再说。
另外,浏览器返回的是第一个满足条件的元素。而QW.Selector在有“,”关系符时,由于没有排序,所以不能保证返回的是第一个。


代码
/**
* 判断一个元素是否符合某selector.
* @method test
* @static
* @param {HTMLElement} el: 被考察参数
* @param {string} sSelector: 过滤selector,这个selector里没有关系运算符(", >+~")
* @returns {function} : 返回过滤函数。
*/
test:
function(el,sSelector){
//...
}

 


test(el,sSelector)方法是判断一个元素是否满足一个最简单的selector。
例如元素el=<div class="aaa bbb"/>满足test(el,'div.aaa'),但是不满足test(el,'div.ccc')。

代码
/**
* 用一个css selector来过滤一个数组.
* @method filter
* @static
* @param {Array|Collection} els: 元素数组
* @param {string} sSelector: 过滤selector,这个selector里没有关系运算符(", >+~")
* @param {Element} pEl: 父节点。默认是document.documentElement
* @returns {Array} : 返回满足过滤条件的元素组成的数组。
*/
filter:
function(els,sSelector,pEl){
//...
},

 


filter(els,sSelector,pEl)是以pEl为参考对象,用sSelector来过滤els。
在备注文档里说是“sSelector: 过滤selector,这个selector里没有关系运算符(", >+~")”
是因为目前代码还没有考虑完全。例如:如果第一个关系符是“+”或“~”时。
QWrap的dom模块里,某些情况下,有使用“>div aaa”等存在关系符的情形。

代码
/**
* 把一个selector字符串转化成一个过滤函数.
* @method selector2Filter
* @static
* @param {string} sSelector 过滤selector,这个selector里没有关系运算符(", >+~")
* @returns {function} : 返回过滤函数。
* @example:
var fun=selector2Filter("input.aaa");alert(fun);
*/
selector2Filter:
function(sSelector){
return s2f(sSelector);
},

 


selector2Filter(sSelector)是把一个最简的selector字符串转化成一个过滤函数。
例如,selector2Filter("div[readOnly]"),会返回以下function:
function(el){
    return el.tagName=='DIV' && !!el.readOnly;
}
在QW.NodeH里的以下方法中会用到
QW.NodeH.ancestorNode(el,sSelector)
QW.NodeH.nextSibling(el,sSelector)
QW.NodeH.previsouSibling(el,sSelector)
例如。QW.NodeH.previsouSibling(el,'div')会返回以el为参考元素,离他最近的<div>兄长。

另外,Selector的效率,一直是一个很热的话题。
QWrap里面也有效率比赛。参见resource/js/_tools/speedmatch/_examples/SelectorSpeed.html
QWrap的selector的效率,在不改代码结构的情形下,还可以进行改进,不过有时很懒,觉得现在在使用的项目中,没人报效率问题,所以,就把它给忽略了。

附:
1. QWrap:http://github.com/wedteam/qwrap

2. QW.Selector的无依赖版代码:

代码
/*
Copyright (c) 2009, Baidu Inc. All rights reserved.
version: $version$ $release$ released
author: yingjiakuan@baidu.com
*/

var QW={};

/**
* @class Selector Css Selector相关的几个方法
* @singleton
* @namespace QW
*/
(
function(){
var trim=function(s){
return s.replace(/^\s+|\s+$/g, "");
},
mulReplace
=function (s,arr){
for(var i=0;i<arr.length;i++) s=s.replace(arr[i][0],arr[i][1]);
return s;
},
encode4Js
=function(s){
return mulReplace(s,[
[
/\\/g,"\\u005C"],
[
/"/g,"\\u0022"],
[
/'/g,"\\u0027"],
[
/\//g,"\\u002F"],
[/\r/g,"\\u000A"],
[
/\n/g,"\\u000D"],
[
/\t/g,"\\u0009"]
]);
};

var Selector={
/**
* @property {int} queryStamp 最后一次查询的时间戳,扩展伪类时可能会用到,以提速
*/
queryStamp:
0,
/**
* @property {Json} _operators selector属性运算符
*/
_operators:{
//以下表达式,aa表示attr值,vv表示比较的值
'': 'aa',//isTrue|hasValue
'=': 'aa=="vv"',//equal
'!=': 'aa!="vv"', //unequal
'~=': 'aa&&(" "+aa+" ").indexOf(" vv ")>-1',//onePart
'|=': 'aa&&(aa+"-").indexOf("vv-")==0', //firstPart
'^=': 'aa&&aa.indexOf("vv")==0', // beginWith
'$=': 'aa&&aa.lastIndexOf("vv")==aa.length-"vv".length', // endWith
'*=': 'aa&&aa.indexOf("vv")>-1' //contains
},
/**
* @property {Json} _shorthands 缩略写法
*/
_shorthands: [
[
/\#([\w\-]+)/g,'[>Selector;
})();

 

/*
Copyright (c) 2009, Baidu Inc. All rights reserved.
version: $version$ $release$ released
author: yingjiakuan@baidu.com
*/

var QW={};

/**
* @class Selector Css Selector相关的几个方法
* @singleton
* @namespace QW
*/
(
function(){
var trim=function(s){
return s.replace(/^\s+|\s+$/g, "");
},
mulReplace
=function (s,arr){
for(var i=0;i<arr.length;i++) s=s.replace(arr[i][0],arr[i][1]);
return s;
},
encode4Js
=function(s){
return mulReplace(s,[
[
/\\/g,"\\u005C"],
[
/"/g,"\\u0022"],
[
/'/g,"\\u0027"],
[
/\//g,"\\u002F"],
[/\r/g,"\\u000A"],
[
/\n/g,"\\u000D"],
[
/\t/g,"\\u0009"]
]);
};

var Selector={
/**
* @property {int} queryStamp 最后一次查询的时间戳,扩展伪类时可能会用到,以提速
*/
queryStamp:
0,
/**
* @property {Json} _operators selector属性运算符
*/
_operators:{
//以下表达式,aa表示attr值,vv表示比较的值
'': 'aa',//isTrue|hasValue
'=': 'aa=="vv"',//equal
'!=': 'aa!="vv"', //unequal
'~=': 'aa&&(" "+aa+" ").indexOf(" vv ")>-1',//onePart
'|=': 'aa&&(aa+"-").indexOf("vv-")==0', //firstPart
'^=': 'aa&&aa.indexOf("vv")==0', // beginWith
'$=': 'aa&&aa.lastIndexOf("vv")==aa.length-"vv".length', // endWith
'*=': 'aa&&aa.indexOf("vv")>-1' //contains
},
/**
* @property {Json} _shorthands 缩略写法
*/
_shorthands: [
[
/\#([\w\-]+)/g,'[>Selector;
})();

相关文章:

  • 2021-05-14
  • 2021-12-25
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-11-20
  • 2021-08-27
  • 2021-11-15
猜你喜欢
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-12-29
  • 2022-12-23
  • 2021-06-20
  • 2022-12-23
相关资源
相似解决方案