# NES
**Repository Path**: mirrors/NES
## Basic Information
- **Project Name**: NES
- **Description**: NES 是网易公司的小型 JS 选择器,高伸缩性,高性能
- **Primary Language**: JavaScript
- **License**: MIT
- **Default Branch**: master
- **Homepage**: https://www.oschina.net/p/nes
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2020-09-11
- **Last Updated**: 2026-02-21
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
#nes
nes is a javascript selector lib with incredible scalability, but still very fast
## 1. API
标准API有:
1. [__one__](#one)
2. [__all__](#all)
3. [__matches__](#matches)
分别对应[JS selector level 2](http://www.w3.org/TR/selectors-api2/)的 __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__
```javascript
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进行判断
```javascript
container.addEventListener("click", function(e){
if(nes.matches(e.target, ".signup a.top")){//直接利用选择器判断是否是注册表单下的置顶按钮
//_onTop() ==> 处理逻辑
}
}
},false)
```
--------------------------------------------------------
## 2. 使用
### 下载
[nes.js](https://raw.github.com/leeluolee/nes/master/nes.js) —— 40K(完整源码注释, 请压缩... gzip+minify 约3~4k)
### 加载
1. 直接插入
```html
```
2. NEJ
```javascript
// 添加选择器适配模块({lib}util/query/query.js)的依赖, then
_v._$addEvent('#home > li', 'click', fn)
// 具体请参考http://nej.netease.com
```
3. AMD
```javascript
define(['/path/to/nes'], function(nes){
nes.all(...) // 方便
})
```
### 选择器支持度
移步[Wiki页](https://github.com/leeluolee/nes/wiki/Selector)
--------------------------------------
## 3. 扩展
扩展是nes真正与众不同的部分到来, 使它即使在未来浏览器对querySelector API支持较好的情况下仍有存在价值。nes没有对规范外的选择器做支持, 而采用库外扩展的方式,部分扩展请参见[extend目录](https://github.com/leeluolee/nes/tree/master/extend)
下面会以 __场景描述__>__解决__ 的形式介绍这几种扩展,这些场景都是建立在即使浏览器已经实现了querySelector的前提下, 用来证明选择器扩展的价值所在
---------------------------------------------------------------
__选择器扩展分为4类__:
1. 对伪类的扩展([pesudos](#pesudos))
2. 对属性的扩展([operators](#operators))
3. 对连接符的扩展([combos](#combos))
4. 通过修改parser直接创建与id、pesudos这些等价的Simple Selector([parser](#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标签
__原始做法__:
```javascript
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
```
__理想做法__:
```javascript
nes.all('ul:include(li.trigger a[href])')
```
__你需要做的扩展是__:
```javascript
// 其中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竟然不提供__不等于__操作符的支持(!=),让我们用一行代码搞定它
__理想做法__
```javascript
nes.all('div[class!=made_up]')
```
__你需要做的扩展是__
```javascript
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') // =>上一个满足的直接兄弟节点
```
__你需要做的扩展是__:
```javascript
// 这里直接一起扩展了~、+的相反版
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页](https://github.com/leeluolee/nes/wiki/parser)
## 4. 测试
test case大部分来自Sizzle, 但是由于sizzle的设计并不是遵循规范的css selector 所以忽视部分在速度测试中sizzle不支持的选择器
### 速度测试
[sizzle目录](https://github.com/leeluolee/nes/tree/master/sizzle)剽窃了sizzle的速度测试用例,不能本地运行, 请祭起你的Server服务,推荐使用[puer](https://github.com/leeluolee/puer)
### 单元测试
[test目录](https://github.com/leeluolee/nes/tree/master/test)剽窃了Sizzle的test case (欠Sizzle的已然太多...)
-------------------------------------------------------
## 5. 贡献代码
Push前的 __注意点__:
1. 修改前开个 __Issues__讨论下总是好的
2. `docs`文件夹中我存放了注释详尽的docco文档,请先通览一次再进行有目的的修改
3. 提交前请确认单元测试在IE6+(各种壳)以及其它现代浏览器是跑通的
4. 对于Bug Fix 请先在测试的`index.html`尾部加入你的测试节点(id为 bug issues),千万不要删改其它test case的节点
## 6. 其它
### 感谢
Q.js、Sizzle、nwmatcher等前辈...还有百度UX那篇选择器扫盲贴。
### 几个可能会用到的配置属性
1. `nes.debug`: 不使用原生querySelector
2. `nes.nthCache.length` : 控制最大nth伪类参数的parse缓存,默认100
### changelog
1. v0.05 - 2013/1/4 抽象出了parser部分
### TODO
1. 我靠 是pseudo 不是pesudo