-
Notifications
You must be signed in to change notification settings - Fork 13
Chain
_2013/3/21 chain API 删除了$click、_$mouseover等生成方法,避免在使用NEJ混淆工具时发生错误 __
nej-chainable 是 基于nej 的 类jQuery 封装, 命名空间 nej.$, 编码风格与nej规范保持一致, API风格类似自jQuery, 主要是为了提供给熟悉jQuery用户类似的操作体验
jQuery的API特征是:
- 身兼数职的简化接口: (可能并不符合语言词义)
- 尽可能的链式操作
- 一定约定下的集合操作
- 专注DOM
nej-chainalbe也遵循这个特征, 在接口上取长补短。
define('xx模块.js', ['{lib}util/chain/chainable.js'], f);
依赖列表:
-
{lib}util/chain/NodeList.js
: 包含完整$的节点包装类和API,但是不包含 NEJ-import API
// 获得某个节点集, 设置样式
$("#chainable li:nth-child(odd)")._$style({
"background": "#cca",
"cursor": "pointer"
})
// 然后抛弃他们 找他们的下一个位置条件满足是4倍数的兄弟节点并设置样式
._$next(":nth-child(2n)")._$style({
"background": "#a19",
"cursor": "help"
})
// 过滤出其中是4倍数的行,并绑定事件
._$filter(":nth-child(4n)")._$click(function(_e) {
$(this)._$style("background", "#111");
// 并给他们中的第一行设置边框
})._$get(1, true)._$style("border", "3px solid #222")
// 找到父节点div并且有chainable的id并且设置样式
._$parent("div#chainable")._$style({
width: "800px",
left: "300px",
position:"absolute"
// 绑定事件以及代理事件
})._$on({
"click" :function(){
var div = document.createElement("div")
div.innerHTML = "haha插入一行"
//每次点击插入一行
$(this)._$insert(div, "bottom");
},
"mouseover li:nth-child(odd)":function(_e){
this._isLight = !this._isLight;
// 每次点击改变背景色
$(this)._$style("background-color", this._isLight? "#cca":"#331")
}
// 获得样式值
})._$style(["width", "left"])
// 到这里链式结束返回{"width":"80px", "left":"30px"}
更多示例与API介绍请看下方
WARN: 上面两个功能上做了扩展
获取匹配的节点集并返回$NodeList(private)实例
Arguments:
- selector - Selector参数可以是多种可能
- String - 代表一个css选择器(.m-home li), 内部使用nes
- Node - 单Dom节点—— 如getElementById("home")
- Array Like Object- 如另一个$NodeList(被$包装过的)、nes返回的节点列表、原生NodeList(childNodes等)、甚至是一个任意的Array(有重复元素、非节点元素,只要里面有节点)
Example:
// 获得"body"节点, 很明显此时节点集只有一个元素
$("body")
// 找到有class1并且有rel属性的节点集,这时可能会有很多个节点
$("body li.class1[rel]")
// 会过滤出childNodes的节点,其他Array Like也是类似
$(document.body.childNodes)
// 有时候你不确定输入的是什么参数, 安全的再包一次吧,无副作用
$body = $("body")
$body2 = $($body2)
WARNING:
虽然$NodeList表现类似数组, 如:
var $nodes = $("li");
$nodes.length// => 返回包装的总节点树
$nodes[1] // 返回第二个节点
// 甚至在控制台的表现也类似
console.log($nodes)
但是已经不是一个数组, 更不是一个原生的NodeList对象, 所以千万不要去尝试调用数组方法, 这可能是没有使用过jQuery的人容易犯的错误。
Interface:
-
_$style(name)
: 相当于_$getStyle 返回样式值 -
_$style([name1,name2...])
相当于多重_$getStyle 返回一个Object(如{"height:20px, width:30px..."}) -
_$style(name, value)
: 相当于setStyle 返回this -
_$style(obj)
相当于多重版setStyle(即原_$style) 返回this
Interface:
-
_$attr(name)
: 相当于_$attr 返回属性值 -
_$attr([name1, name2])
同style描述 返回{titile:"xxx",rel:"xxx", href:"xxx"} -
_$attr(name, value)
: 相当于_$attr 返回this -
_$attr(obj)
: 相当于多重版的_$attr 返回this
其他NEJ API请参考NEJ文档
通过控制台输出nej.$来获得对NEJ API的移植列表
- nej.e._$addClassNam:
chainable
- nej.e._$delClassNam:
chainable
- nej.e._$hasClassName
- nej.e._$replaceClassName:
chainable
- nej.e._$toggle:
chainable
- nej.e._$setStyle:
chainable
- nej.e._$getStyle
- nej.e._$css3d:
chainable
- nej.e._$offset
- nej.e._$getScrollViewPort
- nej.e._$fixed:
chainable
- nej.e._$effect:
chainable
- nej.e._$fade:
chainable
- nej.e._$focus:
chainable
- nej.e._$highlight:
chainable
- nej.e._$hover:
chainable
- nej.e._$page
- nej.e._$placeholder:
chainable
- nej.e._$tab
- nej.e._$wrapInline
- nej.e._$attr:
chainable
- nej.e._$dataset
- nej.e._$remove:
chainable
- nej.e._$removeByEC:
chainable
- nej.e._$dom2xml
- nej.e._$bindClearAction:
chainable
- nej.e._$bindCopyAction:
chainable
- nej.e._$counter
- nej.v._$addEvent:
chainable
- nej.v._$clearEvent:
chainable
- nej.v._$delEven:
chainable
t - nej.v._$dispatchEvent:
chainable
类似于ES5的Array#forEach, 一个遍历函数
Interface:
- $NodeList _$forEach( Function iterator ): 遍历每个函数并调用iterator
- iterator(Element node, Number index): 遍历函数
- node: 当前遍历到的内部节点
- index: 节点所在下标
- this: this对象指向$NodeList 实例
- iterator(Element node, Number index): 遍历函数
Example:
// 将1,4,7...class含有strong的li元素分别加上阶梯型的高度
$("li.strong:nth-child(3n+1)")._$forEach(function(_node, _index){
_node.style.height = "" + (_index+1)*10 + "px";
// 这里的this指向实例
})
类似于ES5的Array#filter, 过滤出满足条件的内部节点并返回一个 新的 $NodeList
实例
Interface:
-
$NodeList _$filter(String selector): 通过选择器过滤,
- selector: css选择器
-
$NodeList _$filter(Function iterator): 通过过滤函数iterator过滤,
- iterator(node, index): 返回 Boolean值,代表node是否通过筛选
- node: 当前遍历到的内部节点
- index: 节点所在下标
- this: this对象指向$NodeList 实例
- iterator(node, index): 返回 Boolean值,代表node是否通过筛选
Example:
// 返回节点集中的匹配选择器.strong:nth-child(3n)的节点
$("li")._$filter(".strong:nth-child(3n)")
// 相当于 ===>
$("li")._$filter(function(_node){
return $(_node)._$matches(".strong:nth-child(3n)");
});
可以看到使用selector可以让调用简洁的多
相当于ES5的Array#map, 当返回值全部是节点类型时,返回$NodeListchainable
, 否则返回标准结果数组(此时chainable不能)
Interface:
- [$NodeList|Array] _$map(Function iterator): 遍历结果
- iterator(node, index): 返回任意值, 结果会被填充到返回数组或$NodeList
- node: 当前遍历到的内部节点
- index: 节点所在下标
- this: this对象指向$NodeList 实例
- iterator(node, index): 返回任意值, 结果会被填充到返回数组或$NodeList
Example:
// 此时返回Array : ["li", "li", "li".........]
$("li")._$map(function(_node){
return _node.tagName.toLowerCase()
});
// 此时返回 $NodeList: 即所有节点的下一个兄弟节点
$("li")._$map(function(_node){
return _node.nextSibling
});
返回对应下标的元素(或包装成$NodeList) , 下标始于0
Interface:
- (Element|$NodeList) _$get([Number index[, bool convert]]): 返回对应
- index: 所需节点的下标, __缺少__则返回(所有元素组成的数组), 相当于this[index]
- convert: 是否将节点转为$NodeList 缺省为false
Example:
//获得第三个元素, 相当于$("li")[2]
$("li")._$get(2);
// 获得第四个元素,并包装成 $NodeList
$("li")._$get(3, true)
// 返回所有元素组成的Array
$("li")._$get()
向内部节点集填入元素, 会处理好重复以及过滤的逻辑。
Interface:
- $NodeList _$add(node):
- node: 可以是节点也可以是上文提到了Array Like Object,会做好过滤
Example:
var $body = $("body")
$body._$add($("tbody")) //==> 添加tbody
$body._$add($("tbody")) //==> 什么都不会发生 因为重复了
$body._$add(document.body.childNodes) //==> 添加所有的body下的子节点,过滤掉不符合的
相当于nes.matches
Interface:
- Boolean _$matches(String selector): 返回__第一个__元素是否满足选择器
- selector: css 选择器
Exameple:
$("body tbody td:nth-child(4n)")._$matches("body tbody td:nth-child(2n)")
// 返回 true... 这个是当然的
NodeList中的节点是不保证按文档顺序的, 你可以手动排序
Interface:
- $NodeList _$sort(): 按集合中的元素按文档顺序排列
Exameple:
$("tr:nth-child(6n+7)")
._$add($("body")) // => ['tr', 'tr'..........'body']最后一个才是body
._$sort() // => ['body','tr'............] 按文档顺序body最先
查找 所有节点 的第一个(或所有)满足关系的 父节点 集, 并返回$NodeList
Interface:
- $NodeList _$parent([String selector[, Boolean all]]):
- selector: 过滤选择器, 不传入则不作过滤, 如果第一个参数是Boolean,则视为all参数
- all: 是否要查找到 所有 父节点匹配, 默认为false, 即找到第一个
Exameple:
$("tr")._$parent()
//=> ['tbody', 'thead'],两个是因为节点集中的tr元素可能在tbody或thead中
$("tr")._$parent("tbody")
//=> ['tbody'] 必须满足tbody
$("tr")._$parent(true)
// =>['tbody', 'thead', 'div', 'body' ....] //会向上查找所有父节点
$("tr")._$parent("tbody, body",true)
// =>['body', 'tbody'] //会向上查找所有父节点,但是必须满足选择器
与_$parent类似,查找 所有节点 的第一个(或所有)满足关系的 向前兄弟节点 (previousSibling)集, 并返回$NodeList
Interface:
- $NodeList _$prev([String selector[, Boolean all]]):
- selector: 过滤选择器, 不传入则不作过滤, 如果第一个参数是Boolean,则视为all参数
- all: 是否要查找到 所有向前匹配 的, 默认为false, 即找到第一个匹配就停止
Exameple:
$("td")._$prev("th[scope=row]", true)
// 返回所有在td之前的th元素, 它们的scope属性为 row
与parent类似,查找 所有节点 的第一个(或所有)满足关系的 向后兄弟节点 (nextSibling)集, 并返回$NodeList
Interface:
略 , 见prev
Exameple:
略, 见prev
查找到 本节点集中 所有节点 的满足选择器关系的 直接子节点 (或 任意层级子节点 )集, 并返回$NodeList
Interface:
- $NodeList _$prev([String selector[, Boolean all]]):
- selector: 过滤选择器, 不传入则不作过滤, 如果第一个参数是Boolean,则视为all参数
- all: 是否要查找到__所有层级__的匹配的子节点, 默认为false, 即只找
WARN: children与parent、prev、next参数一致,但是含义不同。因为节点树向下是一般有多个子节点, 而prev、next、parent只只会有一个直接相临
Exameple:
$("body, table")._$children();
// => 相当于 合并body与table的直接子节点
$("body, table")._$children("div, thead");
// => 只要他们子节点中的div 与 thead元素
$("body, table")._$children(true);
// => 这里会获取所有body下的所有层级的子节点(table也在body中)
$("body, table")._$children("td:not(:last-child, :nth-child(2n))",true);
// => 返回所有层级的td元素并且满足选择器 td:not(:last-child, :nth-child(2n))
所有节点的满足选择器条件的同级节点,不包含本身
Interface:
- $NodeList _$siblings([String selector]):
- selector: 过滤选择器, 不传入则不作过滤
WARN: siblings没有all参数,默认为选择全部(首先是很难定义什么是第一个,可能又要引入参数,这个自由度就不给了)
Exameple:
$("script")._$siblings("title,h2");
// => 返回script的同级节点中的
节点操作主要由两个方法 _$insert
与 _$clone
。其他衍生方法其实都是基于这两个方法
克隆节点集内部的 所有节点, 并返回clone的目标节点集 $NodeList 实例
Interface:
- $NodeList _$clone([Boolean withContent]):
- withContent: 是否克隆内部内容,默认为 false
Exmaple:
$("tbody tr:nth-child(2n)")._$clone(); //clone整个偶数行的tr, 无内容
$("tbody")._$clone(true); // clone整个tbody 并带内容
clone会做属性处理,但是不会处理事件绑定
在本节点集的 所有节点 的某个位置(参数决定)插入节点
Interface:
- $NodeList _$insert(selector, [String direct]): 将selector决定的节点插入到内部每个节点中(内部有多个则clone)
- selector: 包装器$支持的输入,目的是查找到目标节点, 如果节点、选择器...
- direct : 插入方向分为4种, 默认为bottom, 未知字符串也认为是bottom
- "bottom" : 插入本节点的内部最下方(相当于appendChild)
- "top" : 插入到最上方
- "before" : 插入到本节点前方
- "after" : 插入到本节点后方
Exameple:
//将最下方的tr插到上方
$("tbody")._$insert("tbody tr:last-child", "top");
$("tbody tr:nth-child(3n+1)")._$insert("tbody tr:last-child", "before");
WARN: 有时候我们是想把某个节点插入到别的节点,进行这个节点的链式该怎么办?—— 请看_$insert2 (意思是_$insert to)
在目标节点集(参数决定)的 所有节点 的某个位置插入 本节点集的第一个节点
Exmaples: 略: 就是insert的相反版
分别是insert、insert2的缩写, 如:
-
_$bottom(selector)
相当于_$insert(selector, "bottom")
-
_$top2(selector)
相当于_$insert2(selector, "top")
...其他依次类推
Exameple:
略
完整列表:
- _$bottom
- _$bottom2
- _$top
- _$top2
- _$before
- _$before2
- _$after
- _$after2
获得节点集中的 第一个元素 的innerHTML,或设置 __所有元素__的innerHTML
Interface:
- ($NodeList|String) _$html([String content]):
- content: 缺少content参数时候为get 第一个元素 的innerHTML 此时不可能链式操作。反之为设置 所有元素 的innerHTML可以链式操作
Example:
$("title,h2")._$html("haha")
// 同时设置title与h2的innerHTML为haha
$("title,h2")._$html("haha")
// 获得title(第一个元素)的innerHTML
与 _$html类似, 不过这个操作的是文本内容。
Interface:
略
与 _$html类似, 不过这个操作的是表单元素的value值
Event基本是在jQuery的同名接口的转移, 去掉了所有基本类接口如delegate
、live
、bind
、unbind
、unlive
、undelegate
等(说实话我也不太清楚区别), 全部整合到了 _$on
与_$off
接口中,并做了适当增强。
这部分有比较大的 参数自由度
为 每一个节点 绑定事件回调
事件对象标准化 :
以下标准事件对象属性(或方法)被标准化(同jQuery), 可以直接使用:
-
event.target
-
event.relatedTarget
-
event.metaKey
-
event.which 获取键盘事件的 keyCode 以及鼠标事件的 button如 1,2,3(分别是左中右)
-
event.pageX
-
event.pageY
-
event.stopPropagation();
-
event.preventDefault();
Interface:
-
_$on(String|Array type[, String selector], Function handler)
- type: 事件类型有三种可能输入
- String无空格: 如'click', 则为常规事件类型
- String有空格: 如'click h2.m-title',则空格后的部分视为selector参数
- Array: 如["click", "click h2.m-title"] 则做所有事件的绑定,注意由于是采用的多级过滤,所以可以安全的传入type与selector的组合
- selector: 可选,有则视为事件代理(delegate)。符合selector的子节点会触发事件
- handler: 必须传入,事件回调;
- e: 事件对象 __此对象__已经被部分patch, 见上面表述
- this: 回调的this对象指向绑定节点, 代理事件则指向需代理的节点(如h2.m-title节点)
- type: 事件类型有三种可能输入
-
_$on(Object definition): 多重事件绑定, 类似上面介绍的attr等接口
- definition: 键值对 {type1:handler1,type2:handler2};
- type: 同上面的type 可以是'click'或 'click h2.m-title', 由于js中引用类型无法作为键值, 所以无法传入Array
- handler: 同上面的handler
- definition: 键值对 {type1:handler1,type2:handler2};
上面的组合看起来很多其实很好理解, 下面的例子可以解释
Example:
// 1. 普通事件绑定
$("body")._$on("click", function(_e){
alert("单个事件绑定"+_e.type)
})
// 2. 多个普通类型绑定到同一个handler
$("body")._$on(["click", "mouseover"], function(_e){
alert("多个type绑定"+_e.type)
})
// 3. 单个代理事件绑定 这里等同于_$on("click", "tr:nth-child(2n)", handler)
$("body")._$on("click tr:nth-child(2n)", function(_e){
// this 对象指向当前`触发`事件的tr:nth-child(2n)
_e.preventDefault()
alert("单个代理事件绑定"+_e.type)
})
// 4. 多个类事件绑定(同回调), 这里分别是普通事件dblclick与代理事件click tr:nth-child(2n)
$("body")._$on(["dblclick", "click tr:nth-child(2n)"], function(_e){
_e.preventDefault()
alert("多个事件类型绑定"+_e.type)
})
// 5. 多重事件绑定, 这个可以完成模块的事件绑定初始化
$("body")._$on({
"dblclick":function(_e){
alert("多重事件绑定之普通版"+_e.type)
},
"click tr:nth-child(2n)":function(_e){
alert("多重事件绑定之代理版"+_e.type)
}
})
// 6.
Tips :推荐多用事件代理
为 每一个节点 解除事件回调
由于off可以不传入handler,所以参数灵活度比on要更高
Interface:
-
_$off(String|Array type[, String selector][, Function handler])
- type:可选, 事件类型有4种可能输入
- String无空格: 如'click', 则为常规事件类型
- String有空格: 如'click h2.m-title',则空格后的部分视为selector参数,如h2.m-title
- Array: 如["click", "click h2.m-title"] 则做所有传入type的解除绑定
- undefind: 如果不输入则清空所有节点事件绑定
- selector: 可选,有则视为事件代理解除(undelegate)解除某个type与selector组合的代理事件,无则为普通事件解绑
- handler: 可选,不传入则解除同一type(和selector组合)的所有handler
- type:可选, 事件类型有4种可能输入
-
_$off(Object definition): 多重事件解绑, 类似上面介绍的attr等接口
- definition: 键值对 {type1:handler1,type2:handler2}
- type: 同上面的type 可以是'click'或 'click h2.m-title', 由于js中引用类型无法作为键值, 所以无法传入Array
- handler: 同上面的handler
- definition: 键值对 {type1:handler1,type2:handler2}
Example:
// 1. 普通事件解绑
$("body")._$off("click", handler)
// 2. 多个普通类型事件解绑(同一个handler)
$("body")._$off(["click", "mouseover"], handler)
// 3. 普通事件清除(即不传入handler) __同时会把节点上click类型的代理事件清除!__
$("body")._$off("click")
// 4. 多个普通事件清除 __同时会把节点上相应类型的代理事件清除!__
$("body")._$off(["click","mouseover"])
// 5. 单个代理事件的解绑
$("body")._$off("click","tr:nth-child(2n)", handler)
// 或
$("body")._$off("click tr:nth-child(2n)", handler)
// 6. 多个代理事件的解绑(同一个handler)
$("body")._$off(["dblclick td[title]", "click tr:nth-child(2n)"], handler)
// 7. 代理事件的清除
$("body")._$off("dblclick","td[title]");
$("body")._$off("dblclick td[title]");
$("body")._$off(["dblclick td[title]", "click tr:nth-child(2n)"])
// 8. 多重事件解绑
$("body")._$off({
"dblclick td":handler1,
"click tr":handler2
});
// 9. 所有事件清除
$("body")._$off() //慎重
触发 每个节点 的对应事件, 同 _$dispatchEvent
Interface && Examples:
//触发一个事件
_$trigger("click", params)
//一次触发多个事件(如果有参数,他们共用这个参数)
_$trigger(["click", "mouseover"], params)
// 触发多个事件有不同参数
_$trigger({
"click": params1,
"dblclick": params2
})
TODO: 触发指定的的代理事件
为click等事件提供缩写方法调用
Exmaple:
$("body")._$on("click", handler)
// => 相当于
$("body")._$click(handler)
// => 你也可以这样
$("body")._$click({
"li:first-child" : handler1,
"ul" : handler2
})
完整shortcuts列表:
- _$click
- _$dbclick
- _$blur
- _$change
- _$focus
- _$focusin
- _$focusout
- _$keydown
- _$keypress
- _$keyup
- _$mousedown
- _$mouseup
- _$mouseover
- _$mousemove
- _$mouseout
- _$scroll
- _$select
- _$submit
唯一的扩展接口
Interface
- $._$implement(String name, Function fn [,Object options]):
- name - 方法名
- fn - 扩展方法
- options - 可选参数, 包含
- override - 是否覆盖同名函数, 默认false
- static - 这个方法是否是静态方法(这个很重要) 默认false
- $._$implement(Object def[, Object options]):
- def - 包含方法名和方法的键值对(即多重扩展)
- options - 同上
Example
// 1. 直接扩展
$._$implement("_$hello", function(){
// 遍历容器内的所有节点
this._$forEach(function(_node, _index){
_node.hello = "Hello World"
})
})
$("li:nth-child(2n)")._$hello() // 所有偶数行都被加上了 hello属性
// 2. 直接利用静态方法进行扩展(常用语迁移), 上面扩展等价于
$._$implement("_$hello", function(_node){
_node.hello = "Hello World"
}, {static: true})
// 3. 创建jQuey的wrap 方法
// 创建wrap方法
$._$implement("_$wrap", function(_selector){
var $content = $(_selector)
$content._$after2(this)._$insert(this);
})
$("#list")._$wrap(document.createElement("div"))//
WARN: 当使用静态扩展有如下约定:
- 当返回值为: this 、 传入节点 、 nej.v 、 nej.e 、 undefined (即不返回)时, 视为setter操作,可以进行链式调用, 如上例:
$("li:nth-child(2n)")
._$hello()
._$style({color: "#fff", width: "40px"}) //可继续接其他API
- 当返回值为其他类型时: 视为getter操作, 返回节点列表中 第一个元素 的返回值, 如:
$._$implement("_$title", function(_node){
return _node.title
}, {static: true});
$("li:nth-child(2n) a")._$title()
// => 返回第一个满足条件的a元素的title值
Tips:
其实只是往$NodeList的原型上添加方法, 你也可以利用暴露出来的 $._$fn(全等于$NodeList.prototype)直接添加或覆盖方法(类型jQuery.fn)
- 当只有一个实例、且同一节点无需新创建实例
- event fixture 直接通过event来stop、preventDefault、stopProgation?
- 加入contains方法?
- mouseenter, mouseleave的支持