Skip to content
zhenghaibo edited this page Aug 5, 2013 · 12 revisions

nej-chainable

_2013/3/21 chain API 删除了$click、_$mouseover等生成方法,避免在使用NEJ混淆工具时发生错误 __

nej-chainable基于nej类jQuery 封装, 命名空间 nej.$, 编码风格与nej规范保持一致, API风格类似自jQuery, 主要是为了提供给熟悉jQuery用户类似的操作体验

jQuery的API特征是:

  1. 身兼数职的简化接口: (可能并不符合语言词义)
  2. 尽可能的链式操作
  3. 一定约定下的集合操作
  4. 专注DOM

nej-chainalbe也遵循这个特征, 在接口上取长补短。

使用

1. 引入依赖

define('xx模块.js', ['{lib}util/chain/chainable.js'], f);

依赖列表:

  1. {lib}util/chain/NodeList.js : 包含完整$的节点包装类和API,但是不包含 NEJ-import API

2. 祭起你美元符号,开始操作吧!!

// 获得某个节点集, 设置样式
$("#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介绍请看下方

文档快速导航

实例创建

NEJ-API

WARN: 上面两个功能上做了扩展

Base

Traversing

Manipulation

Events

Attributes

Extension(扩展)

$(selector, [context])

获取匹配的节点集并返回$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的人容易犯的错误。


NEJ移植类API

_$style: chainable*

Interface:

  1. _$style(name): 相当于_$getStyle 返回样式值
  2. _$style([name1,name2...]) 相当于多重_$getStyle 返回一个Object(如{"height:20px, width:30px..."})
  3. _$style(name, value): 相当于setStyle 返回this
  4. _$style(obj) 相当于多重版setStyle(即原_$style) 返回this

WHY? jQuery 的1.9将会有这个特性


_$attr: chainable*

Interface:

  1. _$attr(name): 相当于_$attr 返回属性值
  2. _$attr([name1, name2]) 同style描述 返回{titile:"xxx",rel:"xxx", href:"xxx"}
  3. _$attr(name, value): 相当于_$attr 返回this
  4. _$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: chainablet
  • nej.v._$dispatchEvent: chainable

Base

_$forEach: chainable

类似于ES5的Array#forEach, 一个遍历函数

Interface:

  1. $NodeList _$forEach( Function iterator ): 遍历每个函数并调用iterator
    • iterator(Element node, Number index): 遍历函数
      • node: 当前遍历到的内部节点
      • index: 节点所在下标
      • this: this对象指向$NodeList 实例

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指向实例
})

_$filter: chainable

类似于ES5的Array#filter, 过滤出满足条件的内部节点并返回一个 新的 $NodeList实例

Interface:

  1. $NodeList _$filter(String selector): 通过选择器过滤,

    • selector: css选择器
  2. $NodeList _$filter(Function iterator): 通过过滤函数iterator过滤,

    • iterator(node, index): 返回 Boolean值,代表node是否通过筛选
      • node: 当前遍历到的内部节点
      • index: 节点所在下标
      • this: this对象指向$NodeList 实例

Example:

// 返回节点集中的匹配选择器.strong:nth-child(3n)的节点
$("li")._$filter(".strong:nth-child(3n)") 

// 相当于  ===>
$("li")._$filter(function(_node){
    return $(_node)._$matches(".strong:nth-child(3n)");
});

可以看到使用selector可以让调用简洁的多


_$map: chainable*

相当于ES5的Array#map, 当返回值全部是节点类型时,返回$NodeListchainable, 否则返回标准结果数组(此时chainable不能)

Interface:

  1. [$NodeList|Array] _$map(Function iterator): 遍历结果
    • iterator(node, index): 返回任意值, 结果会被填充到返回数组或$NodeList
      • node: 当前遍历到的内部节点
      • index: 节点所在下标
      • this: this对象指向$NodeList 实例

Example:

// 此时返回Array : ["li", "li", "li".........]
$("li")._$map(function(_node){
    return _node.tagName.toLowerCase()
});
// 此时返回 $NodeList: 即所有节点的下一个兄弟节点
$("li")._$map(function(_node){
    return _node.nextSibling
});

_$get: chainable*

返回对应下标的元素(或包装成$NodeList) , 下标始于0

Interface:

  1. (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()

_$add: chainable

向内部节点集填入元素, 会处理好重复以及过滤的逻辑。

Interface:

  1. $NodeList _$add(node):
    • node: 可以是节点也可以是上文提到了Array Like Object,会做好过滤

Example:

var $body = $("body")

$body._$add($("tbody")) //==> 添加tbody
$body._$add($("tbody")) //==> 什么都不会发生 因为重复了

$body._$add(document.body.childNodes) //==> 添加所有的body下的子节点,过滤掉不符合的

_$matches:

相当于nes.matches

Interface:

  1. Boolean _$matches(String selector): 返回__第一个__元素是否满足选择器
    • selector: css 选择器

Exameple:

$("body tbody td:nth-child(4n)")._$matches("body tbody td:nth-child(2n)")
// 返回 true... 这个是当然的

_$sort: chainable

NodeList中的节点是不保证按文档顺序的, 你可以手动排序

Interface:

  1. $NodeList _$sort(): 按集合中的元素按文档顺序排列

Exameple:

$("tr:nth-child(6n+7)")
    ._$add($("body")) // => ['tr', 'tr'..........'body']最后一个才是body
    ._$sort()         // => ['body','tr'............] 按文档顺序body最先

Traversing

_$parent: chainable

查找 所有节点 的第一个(或所有)满足关系的 父节点 集, 并返回$NodeList

Interface:

  1. $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'] //会向上查找所有父节点,但是必须满足选择器

_$prev: chainable

与_$parent类似,查找 所有节点 的第一个(或所有)满足关系的 向前兄弟节点 (previousSibling)集, 并返回$NodeList

Interface:

  1. $NodeList _$prev([String selector[, Boolean all]]):
    • selector: 过滤选择器, 不传入则不作过滤, 如果第一个参数是Boolean,则视为all参数
    • all: 是否要查找到 所有向前匹配 的, 默认为false, 即找到第一个匹配就停止

Exameple:

$("td")._$prev("th[scope=row]", true) 
// 返回所有在td之前的th元素, 它们的scope属性为 row

_$next: chainable

与parent类似,查找 所有节点 的第一个(或所有)满足关系的 向后兄弟节点 (nextSibling)集, 并返回$NodeList

Interface:

略 , 见prev

Exameple:

略, 见prev


_$children: chainable

查找到 本节点集中 所有节点 的满足选择器关系的 直接子节点 (或 任意层级子节点 )集, 并返回$NodeList

Interface:

  1. $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))

_$siblings: chainable

所有节点的满足选择器条件的同级节点,不包含本身

Interface:

  1. $NodeList _$siblings([String selector]):
    • selector: 过滤选择器, 不传入则不作过滤

WARN: siblings没有all参数,默认为选择全部(首先是很难定义什么是第一个,可能又要引入参数,这个自由度就不给了)

Exameple:

$("script")._$siblings("title,h2");
// => 返回script的同级节点中的

Manipulation

节点操作主要由两个方法 _$insert_$clone。其他衍生方法其实都是基于这两个方法

_$clone: chainable

克隆节点集内部的 所有节点, 并返回clone的目标节点集 $NodeList 实例

Interface:

  1. $NodeList _$clone([Boolean withContent]):
    • withContent: 是否克隆内部内容,默认为 false

Exmaple:

$("tbody tr:nth-child(2n)")._$clone(); //clone整个偶数行的tr, 无内容

$("tbody")._$clone(true); // clone整个tbody 并带内容

clone会做属性处理,但是不会处理事件绑定

_$insert: chainable

在本节点集的 所有节点 的某个位置(参数决定)插入节点

Interface:

  1. $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)


_$insert2: chainable

在目标节点集(参数决定)的 所有节点 的某个位置插入 本节点集的第一个节点

Exmaples: 略: 就是insert的相反版


insert-shortcuts: chainable

分别是insert、insert2的缩写, 如:

  • _$bottom(selector) 相当于 _$insert(selector, "bottom")
  • _$top2(selector) 相当于 _$insert2(selector, "top")

...其他依次类推

Exameple:

完整列表:

  • _$bottom
  • _$bottom2
  • _$top
  • _$top2
  • _$before
  • _$before2
  • _$after
  • _$after2

Attributes

_$html: chainable*

获得节点集中的 第一个元素 的innerHTML,或设置 __所有元素__的innerHTML

Interface:

  1. ($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

_$text([String content]): chainable*

与 _$html类似, 不过这个操作的是文本内容。

Interface:


_$val([String value]): chainable*

与 _$html类似, 不过这个操作的是表单元素的value值


Event

Event基本是在jQuery的同名接口的转移, 去掉了所有基本类接口如delegatelivebindunbindunliveundelegate等(说实话我也不太清楚区别), 全部整合到了 _$on_$off接口中,并做了适当增强。

这部分有比较大的 参数自由度

_$on: chainable

每一个节点 绑定事件回调

事件对象标准化 :

以下标准事件对象属性(或方法)被标准化(同jQuery), 可以直接使用:

  • event.target

  • event.relatedTarget

  • event.metaKey

  • event.which 获取键盘事件的 keyCode 以及鼠标事件的 button如 1,2,3(分别是左中右)

  • event.pageX

  • event.pageY

  • event.stopPropagation();

  • event.preventDefault();

Interface:

  1. _$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节点)
  2. _$on(Object definition): 多重事件绑定, 类似上面介绍的attr等接口

    • definition: 键值对 {type1:handler1,type2:handler2};
      • type: 同上面的type 可以是'click'或 'click h2.m-title', 由于js中引用类型无法作为键值, 所以无法传入Array
      • handler: 同上面的handler

上面的组合看起来很多其实很好理解, 下面的例子可以解释

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: chainable

每一个节点 解除事件回调

由于off可以不传入handler,所以参数灵活度比on要更高

Interface:

  1. _$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
  2. _$off(Object definition): 多重事件解绑, 类似上面介绍的attr等接口

    • definition: 键值对 {type1:handler1,type2:handler2}
      • type: 同上面的type 可以是'click'或 'click h2.m-title', 由于js中引用类型无法作为键值, 所以无法传入Array
      • handler: 同上面的handler

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() //慎重

_$trigger: chainable

触发 每个节点 的对应事件, 同 _$dispatchEvent

Interface && Examples:

//触发一个事件
_$trigger("click", params) 
//一次触发多个事件(如果有参数,他们共用这个参数)
_$trigger(["click", "mouseover"], params) 
// 触发多个事件有不同参数
_$trigger({
    "click": params1,
    "dblclick": params2
})

TODO: 触发指定的的代理事件


event shortcuts: chainable

为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

Extension

$._$implement

唯一的扩展接口

Interface

  1. $._$implement(String name, Function fn [,Object options]):
    • name - 方法名
    • fn - 扩展方法
    • options - 可选参数, 包含
      • override - 是否覆盖同名函数, 默认false
      • static - 这个方法是否是静态方法(这个很重要) 默认false
  2. $._$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.vnej.eundefined (即不返回)时, 视为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)

TODOLIST

  1. 当只有一个实例、且同一节点无需新创建实例
  2. event fixture 直接通过event来stop、preventDefault、stopProgation?
  3. 加入contains方法?
  4. mouseenter, mouseleave的支持