#nes nes is a javascript selector lib with incredible scalability, but still very fast
这是与Sizzle 1.8.x的速度对比图, 你也可以在本地进行测试(sizzle目录下的speed), 需要在根目录下开启一个静态服务器(使用puer)
浏览器支持
- IE6+
1. API
标准API有:
分别对应JS selector level 2的 querySelector、 querySelectorAll 与 matches (暂时浏览器还不支持)
1.1 nes.one(selector[, context])
返回 __第一个__匹配selector(在context的subtree中)的元素
1.2 nes.all(selector[, context])
返回 __所有__满足selector(在context的subtree中)的元素, 并按文档顺序排好
Arguments
- selector - 满足css选择器语法的字符串
- context(optional) - context限定节点查找的范围(缺省为document)
Example
nes.all("tr:nth-child(even) > td:nth-child(odd)") //-> 取得所有偶数列中奇数行
1.3 nes.matches(node, selector)
判断节点node是否满足特定的选择器selector
Arguments
- node - 目标节点
- selector - 满足css选择器语法的字符串
Example
如利用事件代理时,你不需要再去调用标准dom方法去测试节点是否满足某种条件,直接使用matches进行判断
container.addEventListener("click", function(e){
if(nes.matches(e.target, ".signup a.top")){//直接利用选择器判断是否是注册表单下的置顶按钮
//_onTop() ==> 处理逻辑
}
}
},false)
2. 使用
下载
nes.js —— 40K(完整源码注释, 请压缩... gzip+minify 约3~4k)
加载
- 直接插入
<script src="/path/to/nes.js"></script>
<script>
nes.one(...) //直接注册在全局
</script>
- NEJ
// 添加选择器适配模块({lib}util/query/query.js)的依赖
- AMD
define(['/path/to/nes'], function(nes){
nes.all(...) // 方便
})
选择器支持度
移步Wiki页
3. 扩展
扩展是nes真正与众不同的部分到来, 使它即使在未来浏览器对querySelector API支持较好的情况下仍有存在价值。nes没有对规范外的选择器做支持, 而采用库外扩展的方式,部分扩展请参见extend目录
下面会以 场景描述>解决 的形式介绍这几种扩展,这些场景都是建立在即使浏览器已经实现了querySelector的前提下, 用来证明选择器扩展的价值所在
选择器扩展分为4类:
- 对伪类的扩展(pesudos)
- 对属性的扩展(operators)
- 对连接符的扩展(combos)
- 通过修改parser直接创建与id、pesudos这些等价的Simple Selector(parser)
伪类扩展 —— nes.pesudos(name, matcher)
Arguments
- name - 伪类名(类似selected、nth-child等)
- matcher(node, param) - 返回boolean值
- node - ,当前匹配到的节点,matcher返回这个节点是否满足要求
- param - 一个字符串代表匹配到的参数, 如nth-child(3)中的 3即为匹配到的参数
场景描述:
你需要获取所有的ul元素,这个元素中包含有满足(li.trigger a[href])的a标签
原始做法:
var lists = document.querySelectorAll("ul")
for(var i = lists.length; i--;){
var list = lists[i]
if(!list.querySelectorAll("li.trigger a[href]")){
lists.splice(i, 1)
}
}
return lists
理想做法:
nes.all('ul:include(li.trigger a[href])')
你需要做的扩展是:
// 其中node表示当前遍历到的节点, param代表pesudo的参数如本例的`li.trigger a[href]`
nes.pesudos("include", function(node, param){
return !!nes.one( param, node) // 返回bool值证明这个节点是否满足条件
})
###属性操作符扩展 —— nes.operators(String name, Function matcher)
- name - 伪类名(类似selected、nth-child等)
- matcher(String value, String nodeValue) - 返回boolean值判断这个节点是否满足要求, 参数有:
- value = 代表匹配到的属性值, 如[title=haha] 的haha
- nodeValue - 匹配到的节点真实属性值
场景描述
坑爹啊,标准selector竟然不提供__不等于__操作符的支持(!=),让我们用一行代码搞定它
理想做法
nes.all('div[class!=made_up]')
你需要做的扩展是
nes.operators("!=", function(node, key, value){
return node.getAttribute(key) !== value
})
连接符扩展 —— nes.combos(String name,Function finder)
- name - 伪类名(类似selected、nth-child等)
- matcher(String value, String nodeValue) - 返回boolean值判断这个节点是否满足要求, 参数有:
- value = 代表匹配到的属性值, 如[title=haha] 的haha
- nodeValue - 匹配到的节点真实属性值
__注意:__combo的扩展与上面两个扩展都不同,因为它是连接符而不是前两个的Simple Selector,它传入的是finder函数,目的是找到你满足的节点
场景描述: 你需要获得 ul.test li.trigger
节点 前的所有li节点(即连接符~
的相反版)
原生方法:
&$@&$&@($(!)!)##!&$^!@#$%^&(&^%$#@#$%^&()(%$!@#$% (真的很烦,你们可以私下去尝试下)
理想做法:
nes.all ('ul.test1 li.trigger & li') // =>向上第一个满足的兄弟节点
nes.all ('ul.test1 li.trigger + li') // =>上一个满足的直接兄弟节点
你需要做的扩展是:
// 这里直接一起扩展了~、+的相反版
nes.combos({
// 相当于 ~ 的相反版 , match是一个动态产生的方法,它代表这个节点,是否满足选择器条件,
// nes在match里封装了所有的递归操作,你无需考虑复杂的选择器匹配
// 但是你仍然要告诉nes,你要找的是哪个元素,比如~要做的是: 1)找到前面中的节点 2)
// 这个节点满足剩余的选择器,你在扩展里需要描述清楚的就是这个匹配的节点
"&":function(node,match){
while(node = node.nextSibling){
if(node.nodeType ===1 && match(node)){
return node // 如果节点是元素节点,并且满足match匹配规则
}
}
return null //如果没有则返回null,此轮匹配结束
},
// 与 + 相反
"%":function(node,match){
while(node = node.nextSibling){
if(node.nodeType ===1) return match(node)? node :null
}
}
})
这样做是为了提供给开发者最大的自由度和最小的代码开销(匹配逻辑都封装在了match这个运行时差生的函数)
扩展内部parser —— nes.parser
nes在v0.05版本抽象出了parser部分,你可以通过parser来深层次的修改nes而不需要修改源代码, 具体请见parser的WIKI页
4. 测试
test case大部分来自Sizzle, 但是由于sizzle的设计并不是遵循规范的css selector 所以忽视部分在速度测试中sizzle不支持的选择器
速度测试
sizzle目录剽窃了sizzle的速度测试用例,不能本地运行, 请祭起你的Server服务,推荐使用puer
单元测试
test目录剽窃了Sizzle的test case (欠Sizzle的已然太多...)
5. 贡献代码
Push前的 注意点:
-
修改前开个 __Issues__讨论下总是好的
-
docs
文件夹中我存放了注释详尽的docco文档,请先通览一次再进行有目的的修改 -
提交前请确认单元测试在IE6+(各种壳)以及其它现代浏览器是跑通的
-
对于Bug Fix 请先在测试的
index.html
尾部加入你的测试节点(id为 bug issues),千万不要删改其它test case的节点
6. 其它
感谢
Q.js、Sizzle、nwmatcher等前辈...还有百度UX那篇选择器扫盲贴。
几个可能会用到的配置属性
nes.debug
: 不使用原生querySelectornes.nthCache.length
: 控制最大nth伪类参数的parse缓存,默认100
changelog
- v0.05 - 2013/1/4 抽象出了parser部分
License
(The MIT License)
Copyright (c) 2012-2013 NetEase, Inc. and mcss contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.