1 /** 2 * @fileOverview definition for node and nodelist 3 * @author yiminghe@gmail.com, lifesinger@gmail.com 4 */ 5 KISSY.add("node/base", function (S, DOM, undefined) { 6 7 var AP = Array.prototype, 8 slice = AP.slice, 9 push = AP.push, 10 makeArray = S.makeArray, 11 isNodeList = DOM._isNodeList; 12 13 /** 14 * @class The NodeList class provides a wrapper for manipulating DOM Node. 15 * use KISSY.all/one to retrieve NodeList instances 16 * @name NodeList 17 */ 18 function NodeList(html, props, ownerDocument) { 19 var self = this, 20 domNode; 21 22 if (!(self instanceof NodeList)) { 23 return new NodeList(html, props, ownerDocument); 24 } 25 26 // handle NodeList(''), NodeList(null), or NodeList(undefined) 27 if (!html) { 28 return undefined; 29 } 30 31 else if (S.isString(html)) { 32 // create from html 33 domNode = DOM.create(html, props, ownerDocument); 34 // ('<p>1</p><p>2</p>') 转换为 NodeList 35 if (domNode.nodeType === DOM.DOCUMENT_FRAGMENT_NODE) { // fragment 36 push.apply(this, makeArray(domNode.childNodes)); 37 return undefined; 38 } 39 } 40 41 else if (S.isArray(html) || isNodeList(html)) { 42 push.apply(self, makeArray(html)); 43 return undefined; 44 } 45 46 else { 47 // node, document, window 48 domNode = html; 49 } 50 51 self[0] = domNode; 52 self.length = 1; 53 return undefined; 54 } 55 56 S.augment(NodeList, 57 /** 58 * @lends NodeList# 59 */ 60 { 61 62 /** 63 * length of nodelist 64 * @type Number 65 */ 66 length:0, 67 68 69 /** 70 * Get one node at index 71 * @param {Number} index Index position. 72 * @return {NodeList} 73 */ 74 item:function (index) { 75 var self = this; 76 if (S.isNumber(index)) { 77 if (index >= self.length) { 78 return null; 79 } else { 80 return new NodeList(self[index]); 81 } 82 } else { 83 return new NodeList(index); 84 } 85 }, 86 87 /** 88 * Add existing node list. 89 * @param {String|HTMLElement[]|NodeList} selector Selector string or html string or common dom node. 90 * @param {String|Array<HTMLElement>|NodeList|HTMLElement|Document} [context] Search context for selector 91 * @param {Number} [index] Insert position. 92 * @return {NodeList} 93 */ 94 add:function (selector, context, index) { 95 if (S.isNumber(context)) { 96 index = context; 97 context = undefined; 98 } 99 var list = NodeList.all(selector, context).getDOMNodes(), 100 ret = new NodeList(this); 101 if (index === undefined) { 102 push.apply(ret, list); 103 } else { 104 var args = [index, 0]; 105 args.push.apply(args, list); 106 AP.splice.apply(ret, args); 107 } 108 return ret; 109 }, 110 111 /** 112 * Get part of node list. 113 * @param {Number} start Start position. 114 * @param {number} end End position. 115 * @return {NodeList} 116 */ 117 slice:function (start, end) { 118 // ie<9 : [1,2].slice(-2,undefined) => [] 119 // ie<9 : [1,2].slice(-2) => [] 120 // fix #85 121 return new NodeList(slice.apply(this, arguments)); 122 }, 123 124 /** 125 * Retrieves the DOMNodes. 126 */ 127 getDOMNodes:function () { 128 return slice.call(this); 129 }, 130 131 /** 132 * Applies the given function to each Node in the NodeList. 133 * @param fn The function to apply. It receives 3 arguments: the current node instance, the node's index, and the NodeList instance 134 * @param [context] An optional context to apply the function with Default context is the current NodeList instance 135 */ 136 each:function (fn, context) { 137 var self = this; 138 139 S.each(self, function (n, i) { 140 n = new NodeList(n); 141 return fn.call(context || n, n, i, self); 142 }); 143 144 return self; 145 }, 146 /** 147 * Retrieves the DOMNode. 148 */ 149 getDOMNode:function () { 150 return this[0]; 151 }, 152 153 /** 154 * return last stack node list. 155 * @return {NodeList} 156 */ 157 end:function () { 158 var self = this; 159 return self.__parent || self; 160 }, 161 162 /** 163 * Get node list which are descendants of current node list. 164 * @param {String} selector Selector string 165 * @return {NodeList} 166 */ 167 all:function (selector) { 168 var ret, self = this; 169 if (self.length > 0) { 170 ret = NodeList.all(selector, self); 171 } else { 172 ret = new NodeList(); 173 } 174 ret.__parent = self; 175 return ret; 176 }, 177 178 one:function (selector) { 179 var self = this, all = self.all(selector), 180 ret = all.length ? all.slice(0, 1) : null; 181 if (ret) { 182 ret.__parent = self; 183 } 184 return ret; 185 } 186 }); 187 188 S.mix(NodeList, 189 /** 190 * @lends NodeList 191 */ 192 { 193 /** 194 * Get node list from selector or construct new node list from html string. 195 * Can also called from KISSY.all 196 * @param {String|HTMLElement[]|NodeList} selector Selector string or html string or common dom node. 197 * @param {String|Array<HTMLElement>|NodeList|HTMLElement|Document} [context] Search context for selector 198 * @returns {NodeList} 199 */ 200 all:function (selector, context) { 201 // are we dealing with html string ? 202 // TextNode 仍需要自己 new Node 203 204 if (S.isString(selector) 205 && (selector = S.trim(selector)) 206 && selector.length >= 3 207 && S.startsWith(selector, "<") 208 && S.endsWith(selector, ">") 209 ) { 210 if (context) { 211 if (context.getDOMNode) { 212 context = context.getDOMNode(); 213 } 214 if (context.ownerDocument) { 215 context = context.ownerDocument; 216 } 217 } 218 return new NodeList(selector, undefined, context); 219 } 220 return new NodeList(DOM.query(selector, context)); 221 }, 222 one:function (selector, context) { 223 var all = NodeList.all(selector, context); 224 return all.length ? all.slice(0, 1) : null; 225 } 226 }); 227 228 S.mix(NodeList, DOM.NodeTypes); 229 230 return NodeList; 231 }, { 232 requires:["dom"] 233 }); 234 235 236 /** 237 * Notes: 238 * 2011-05-25 239 * - 承玉:参考 jquery,只有一个 NodeList 对象,Node 就是 NodeList 的别名 240 * 241 * 2010.04 242 * - each 方法传给 fn 的 this, 在 jQuery 里指向原生对象,这样可以避免性能问题。 243 * 但从用户角度讲,this 的第一直觉是 $(this), kissy 和 yui3 保持一致,牺牲 244 * 性能,以易用为首。 245 * - 有了 each 方法,似乎不再需要 import 所有 dom 方法,意义不大。 246 * - dom 是低级 api, node 是中级 api, 这是分层的一个原因。还有一个原因是,如果 247 * 直接在 node 里实现 dom 方法,则不大好将 dom 的方法耦合到 nodelist 里。可 248 * 以说,技术成本会制约 api 设计。 249 */ 250