NodeList-traverse.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. define("dojo/NodeList-traverse", ["./query", "./_base/lang", "./_base/array"], function(dquery, lang, array) {
  2. // module:
  3. // dojo/NodeList-traverse
  4. // summary:
  5. // TODOC
  6. var NodeList = dquery.NodeList;
  7. /*=====
  8. dojo["NodeList-traverse"] = {
  9. // summary: Adds a chainable methods to dojo.query() / Nodelist instances for traversing the DOM
  10. };
  11. // doc alias helpers:
  12. NodeList = dojo.NodeList;
  13. =====*/
  14. lang.extend(NodeList, {
  15. _buildArrayFromCallback: function(/*Function*/callback){
  16. // summary:
  17. // builds a new array of possibly differing size based on the input list.
  18. // Since the returned array is likely of different size than the input array,
  19. // the array's map function cannot be used.
  20. var ary = [];
  21. for(var i = 0; i < this.length; i++){
  22. var items = callback.call(this[i], this[i], ary);
  23. if(items){
  24. ary = ary.concat(items);
  25. }
  26. }
  27. return ary; //Array
  28. },
  29. _getUniqueAsNodeList: function(/*Array*/ nodes){
  30. // summary:
  31. // given a list of nodes, make sure only unique
  32. // elements are returned as our NodeList object.
  33. // Does not call _stash().
  34. var ary = [];
  35. //Using for loop for better speed.
  36. for(var i = 0, node; node = nodes[i]; i++){
  37. //Should be a faster way to do this. dojo.query has a private
  38. //_zip function that may be inspirational, but there are pathways
  39. //in query that force nozip?
  40. if(node.nodeType == 1 && array.indexOf(ary, node) == -1){
  41. ary.push(node);
  42. }
  43. }
  44. return this._wrap(ary, null, this._NodeListCtor); //dojo.NodeList
  45. },
  46. _getUniqueNodeListWithParent: function(/*Array*/ nodes, /*String*/ query){
  47. // summary:
  48. // gets unique element nodes, filters them further
  49. // with an optional query and then calls _stash to track parent NodeList.
  50. var ary = this._getUniqueAsNodeList(nodes);
  51. ary = (query ? dquery._filterResult(ary, query) : ary);
  52. return ary._stash(this); //dojo.NodeList
  53. },
  54. _getRelatedUniqueNodes: function(/*String?*/ query, /*Function*/ callback){
  55. // summary:
  56. // cycles over all the nodes and calls a callback
  57. // to collect nodes for a possible inclusion in a result.
  58. // The callback will get two args: callback(node, ary),
  59. // where ary is the array being used to collect the nodes.
  60. return this._getUniqueNodeListWithParent(this._buildArrayFromCallback(callback), query); //dojo.NodeList
  61. },
  62. children: function(/*String?*/ query){
  63. // summary:
  64. // Returns all immediate child elements for nodes in this dojo.NodeList.
  65. // Optionally takes a query to filter the child elements.
  66. // description:
  67. // .end() can be used on the returned dojo.NodeList to get back to the
  68. // original dojo.NodeList.
  69. // query:
  70. // a CSS selector.
  71. // returns:
  72. // dojo.NodeList, all immediate child elements for the nodes in this dojo.NodeList.
  73. // example:
  74. // assume a DOM created by this markup:
  75. // | <div class="container">
  76. // | <div class="red">Red One</div>
  77. // | Some Text
  78. // | <div class="blue">Blue One</div>
  79. // | <div class="red">Red Two</div>
  80. // | <div class="blue">Blue Two</div>
  81. // | </div>
  82. // Running this code:
  83. // | dojo.query(".container").children();
  84. // returns the four divs that are children of the container div.
  85. // Running this code:
  86. // | dojo.query(".container").children(".red");
  87. // returns the two divs that have the class "red".
  88. return this._getRelatedUniqueNodes(query, function(node, ary){
  89. return lang._toArray(node.childNodes);
  90. }); //dojo.NodeList
  91. },
  92. closest: function(/*String*/ query, /*String|DOMNode?*/ root){
  93. // summary:
  94. // Returns closest parent that matches query, including current node in this
  95. // dojo.NodeList if it matches the query.
  96. // description:
  97. // .end() can be used on the returned dojo.NodeList to get back to the
  98. // original dojo.NodeList.
  99. // query:
  100. // a CSS selector.
  101. // root:
  102. // If specified, query is relative to "root" rather than document body.
  103. // returns:
  104. // dojo.NodeList, the closest parent that matches the query, including the current
  105. // node in this dojo.NodeList if it matches the query.
  106. // example:
  107. // assume a DOM created by this markup:
  108. // | <div class="container">
  109. // | <div class="red">Red One</div>
  110. // | Some Text
  111. // | <div class="blue">Blue One</div>
  112. // | <div class="red">Red Two</div>
  113. // | <div class="blue">Blue Two</div>
  114. // | </div>
  115. // Running this code:
  116. // | dojo.query(".red").closest(".container");
  117. // returns the div with class "container".
  118. return this._getRelatedUniqueNodes(null, function(node, ary){
  119. do{
  120. if(dquery._filterResult([node], query, root).length){
  121. return node;
  122. }
  123. }while(node != root && (node = node.parentNode) && node.nodeType == 1);
  124. return null; //To make rhino strict checking happy.
  125. }); //dojo.NodeList
  126. },
  127. parent: function(/*String?*/ query){
  128. // summary:
  129. // Returns immediate parent elements for nodes in this dojo.NodeList.
  130. // Optionally takes a query to filter the parent elements.
  131. // description:
  132. // .end() can be used on the returned dojo.NodeList to get back to the
  133. // original dojo.NodeList.
  134. // query:
  135. // a CSS selector.
  136. // returns:
  137. // dojo.NodeList, immediate parent elements for nodes in this dojo.NodeList.
  138. // example:
  139. // assume a DOM created by this markup:
  140. // | <div class="container">
  141. // | <div class="red">Red One</div>
  142. // | <div class="blue first"><span class="text">Blue One</span></div>
  143. // | <div class="red">Red Two</div>
  144. // | <div class="blue"><span class="text">Blue Two</span></div>
  145. // | </div>
  146. // Running this code:
  147. // | dojo.query(".text").parent();
  148. // returns the two divs with class "blue".
  149. // Running this code:
  150. // | dojo.query(".text").parent(".first");
  151. // returns the one div with class "blue" and "first".
  152. return this._getRelatedUniqueNodes(query, function(node, ary){
  153. return node.parentNode;
  154. }); //dojo.NodeList
  155. },
  156. parents: function(/*String?*/ query){
  157. // summary:
  158. // Returns all parent elements for nodes in this dojo.NodeList.
  159. // Optionally takes a query to filter the child elements.
  160. // description:
  161. // .end() can be used on the returned dojo.NodeList to get back to the
  162. // original dojo.NodeList.
  163. // query:
  164. // a CSS selector.
  165. // returns:
  166. // dojo.NodeList, all parent elements for nodes in this dojo.NodeList.
  167. // example:
  168. // assume a DOM created by this markup:
  169. // | <div class="container">
  170. // | <div class="red">Red One</div>
  171. // | <div class="blue first"><span class="text">Blue One</span></div>
  172. // | <div class="red">Red Two</div>
  173. // | <div class="blue"><span class="text">Blue Two</span></div>
  174. // | </div>
  175. // Running this code:
  176. // | dojo.query(".text").parents();
  177. // returns the two divs with class "blue", the div with class "container",
  178. // | the body element and the html element.
  179. // Running this code:
  180. // | dojo.query(".text").parents(".container");
  181. // returns the one div with class "container".
  182. return this._getRelatedUniqueNodes(query, function(node, ary){
  183. var pary = [];
  184. while(node.parentNode){
  185. node = node.parentNode;
  186. pary.push(node);
  187. }
  188. return pary;
  189. }); //dojo.NodeList
  190. },
  191. siblings: function(/*String?*/ query){
  192. // summary:
  193. // Returns all sibling elements for nodes in this dojo.NodeList.
  194. // Optionally takes a query to filter the sibling elements.
  195. // description:
  196. // .end() can be used on the returned dojo.NodeList to get back to the
  197. // original dojo.NodeList.
  198. // query:
  199. // a CSS selector.
  200. // returns:
  201. // dojo.NodeList, all sibling elements for nodes in this dojo.NodeList.
  202. // example:
  203. // assume a DOM created by this markup:
  204. // | <div class="container">
  205. // | <div class="red">Red One</div>
  206. // | Some Text
  207. // | <div class="blue first">Blue One</div>
  208. // | <div class="red">Red Two</div>
  209. // | <div class="blue">Blue Two</div>
  210. // | </div>
  211. // Running this code:
  212. // | dojo.query(".first").siblings();
  213. // returns the two divs with class "red" and the other div
  214. // | with class "blue" that does not have "first".
  215. // Running this code:
  216. // | dojo.query(".first").siblings(".red");
  217. // returns the two div with class "red".
  218. return this._getRelatedUniqueNodes(query, function(node, ary){
  219. var pary = [];
  220. var nodes = (node.parentNode && node.parentNode.childNodes);
  221. for(var i = 0; i < nodes.length; i++){
  222. if(nodes[i] != node){
  223. pary.push(nodes[i]);
  224. }
  225. }
  226. return pary;
  227. }); //dojo.NodeList
  228. },
  229. next: function(/*String?*/ query){
  230. // summary:
  231. // Returns the next element for nodes in this dojo.NodeList.
  232. // Optionally takes a query to filter the next elements.
  233. // description:
  234. // .end() can be used on the returned dojo.NodeList to get back to the
  235. // original dojo.NodeList.
  236. // query:
  237. // a CSS selector.
  238. // returns:
  239. // dojo.NodeList, the next element for nodes in this dojo.NodeList.
  240. // example:
  241. // assume a DOM created by this markup:
  242. // | <div class="container">
  243. // | <div class="red">Red One</div>
  244. // | Some Text
  245. // | <div class="blue first">Blue One</div>
  246. // | <div class="red">Red Two</div>
  247. // | <div class="blue last">Blue Two</div>
  248. // | </div>
  249. // Running this code:
  250. // | dojo.query(".first").next();
  251. // returns the div with class "red" and has innerHTML of "Red Two".
  252. // Running this code:
  253. // | dojo.query(".last").next(".red");
  254. // does not return any elements.
  255. return this._getRelatedUniqueNodes(query, function(node, ary){
  256. var next = node.nextSibling;
  257. while(next && next.nodeType != 1){
  258. next = next.nextSibling;
  259. }
  260. return next;
  261. }); //dojo.NodeList
  262. },
  263. nextAll: function(/*String?*/ query){
  264. // summary:
  265. // Returns all sibling elements that come after the nodes in this dojo.NodeList.
  266. // Optionally takes a query to filter the sibling elements.
  267. // description:
  268. // .end() can be used on the returned dojo.NodeList to get back to the
  269. // original dojo.NodeList.
  270. // query:
  271. // a CSS selector.
  272. // returns:
  273. // dojo.NodeList, all sibling elements that come after the nodes in this dojo.NodeList.
  274. // example:
  275. // assume a DOM created by this markup:
  276. // | <div class="container">
  277. // | <div class="red">Red One</div>
  278. // | Some Text
  279. // | <div class="blue first">Blue One</div>
  280. // | <div class="red next">Red Two</div>
  281. // | <div class="blue next">Blue Two</div>
  282. // | </div>
  283. // Running this code:
  284. // | dojo.query(".first").nextAll();
  285. // returns the two divs with class of "next".
  286. // Running this code:
  287. // | dojo.query(".first").nextAll(".red");
  288. // returns the one div with class "red" and innerHTML "Red Two".
  289. return this._getRelatedUniqueNodes(query, function(node, ary){
  290. var pary = [];
  291. var next = node;
  292. while((next = next.nextSibling)){
  293. if(next.nodeType == 1){
  294. pary.push(next);
  295. }
  296. }
  297. return pary;
  298. }); //dojo.NodeList
  299. },
  300. prev: function(/*String?*/ query){
  301. // summary:
  302. // Returns the previous element for nodes in this dojo.NodeList.
  303. // Optionally takes a query to filter the previous elements.
  304. // description:
  305. // .end() can be used on the returned dojo.NodeList to get back to the
  306. // original dojo.NodeList.
  307. // query:
  308. // a CSS selector.
  309. // returns:
  310. // dojo.NodeList, the previous element for nodes in this dojo.NodeList.
  311. // example:
  312. // assume a DOM created by this markup:
  313. // | <div class="container">
  314. // | <div class="red">Red One</div>
  315. // | Some Text
  316. // | <div class="blue first">Blue One</div>
  317. // | <div class="red">Red Two</div>
  318. // | <div class="blue">Blue Two</div>
  319. // | </div>
  320. // Running this code:
  321. // | dojo.query(".first").prev();
  322. // returns the div with class "red" and has innerHTML of "Red One".
  323. // Running this code:
  324. // | dojo.query(".first").prev(".blue");
  325. // does not return any elements.
  326. return this._getRelatedUniqueNodes(query, function(node, ary){
  327. var prev = node.previousSibling;
  328. while(prev && prev.nodeType != 1){
  329. prev = prev.previousSibling;
  330. }
  331. return prev;
  332. }); //dojo.NodeList
  333. },
  334. prevAll: function(/*String?*/ query){
  335. // summary:
  336. // Returns all sibling elements that come before the nodes in this dojo.NodeList.
  337. // Optionally takes a query to filter the sibling elements.
  338. // description:
  339. // The returned nodes will be in reverse DOM order -- the first node in the list will
  340. // be the node closest to the original node/NodeList.
  341. // .end() can be used on the returned dojo.NodeList to get back to the
  342. // original dojo.NodeList.
  343. // query:
  344. // a CSS selector.
  345. // returns:
  346. // dojo.NodeList, all sibling elements that come before the nodes in this dojo.NodeList.
  347. // example:
  348. // assume a DOM created by this markup:
  349. // | <div class="container">
  350. // | <div class="red prev">Red One</div>
  351. // | Some Text
  352. // | <div class="blue prev">Blue One</div>
  353. // | <div class="red second">Red Two</div>
  354. // | <div class="blue">Blue Two</div>
  355. // | </div>
  356. // Running this code:
  357. // | dojo.query(".second").prevAll();
  358. // returns the two divs with class of "prev".
  359. // Running this code:
  360. // | dojo.query(".first").prevAll(".red");
  361. // returns the one div with class "red prev" and innerHTML "Red One".
  362. return this._getRelatedUniqueNodes(query, function(node, ary){
  363. var pary = [];
  364. var prev = node;
  365. while((prev = prev.previousSibling)){
  366. if(prev.nodeType == 1){
  367. pary.push(prev);
  368. }
  369. }
  370. return pary;
  371. }); //dojo.NodeList
  372. },
  373. andSelf: function(){
  374. // summary:
  375. // Adds the nodes from the previous dojo.NodeList to the current dojo.NodeList.
  376. // description:
  377. // .end() can be used on the returned dojo.NodeList to get back to the
  378. // original dojo.NodeList.
  379. // returns:
  380. // dojo.NodeList
  381. // example:
  382. // assume a DOM created by this markup:
  383. // | <div class="container">
  384. // | <div class="red prev">Red One</div>
  385. // | Some Text
  386. // | <div class="blue prev">Blue One</div>
  387. // | <div class="red second">Red Two</div>
  388. // | <div class="blue">Blue Two</div>
  389. // | </div>
  390. // Running this code:
  391. // | dojo.query(".second").prevAll().andSelf();
  392. // returns the two divs with class of "prev", as well as the div with class "second".
  393. return this.concat(this._parent); //dojo.NodeList
  394. },
  395. //Alternate methods for the :first/:last/:even/:odd pseudos.
  396. first: function(){
  397. // summary:
  398. // Returns the first node in this dojo.NodeList as a dojo.NodeList.
  399. // description:
  400. // .end() can be used on the returned dojo.NodeList to get back to the
  401. // original dojo.NodeList.
  402. // returns:
  403. // dojo.NodeList, with the first node in this dojo.NodeList
  404. // example:
  405. // assume a DOM created by this markup:
  406. // | <div class="container">
  407. // | <div class="red">Red One</div>
  408. // | <div class="blue first">Blue One</div>
  409. // | <div class="red">Red Two</div>
  410. // | <div class="blue last">Blue Two</div>
  411. // | </div>
  412. // Running this code:
  413. // | dojo.query(".blue").first();
  414. // returns the div with class "blue" and "first".
  415. return this._wrap(((this[0] && [this[0]]) || []), this); //dojo.NodeList
  416. },
  417. last: function(){
  418. // summary:
  419. // Returns the last node in this dojo.NodeList as a dojo.NodeList.
  420. // description:
  421. // .end() can be used on the returned dojo.NodeList to get back to the
  422. // original dojo.NodeList.
  423. // returns:
  424. // dojo.NodeList, with the last node in this dojo.NodeList
  425. // example:
  426. // assume a DOM created by this markup:
  427. // | <div class="container">
  428. // | <div class="red">Red One</div>
  429. // | <div class="blue first">Blue One</div>
  430. // | <div class="red">Red Two</div>
  431. // | <div class="blue last">Blue Two</div>
  432. // | </div>
  433. // Running this code:
  434. // | dojo.query(".blue").last();
  435. // returns the last div with class "blue",
  436. return this._wrap((this.length ? [this[this.length - 1]] : []), this); //dojo.NodeList
  437. },
  438. even: function(){
  439. // summary:
  440. // Returns the even nodes in this dojo.NodeList as a dojo.NodeList.
  441. // description:
  442. // .end() can be used on the returned dojo.NodeList to get back to the
  443. // original dojo.NodeList.
  444. // returns:
  445. // dojo.NodeList, with the even nodes in this dojo.NodeList
  446. // example:
  447. // assume a DOM created by this markup:
  448. // | <div class="container">
  449. // | <div class="interior red">Red One</div>
  450. // | <div class="interior blue">Blue One</div>
  451. // | <div class="interior red">Red Two</div>
  452. // | <div class="interior blue">Blue Two</div>
  453. // | </div>
  454. // Running this code:
  455. // | dojo.query(".interior").even();
  456. // returns the two divs with class "blue"
  457. return this.filter(function(item, i){
  458. return i % 2 != 0;
  459. }); //dojo.NodeList
  460. },
  461. odd: function(){
  462. // summary:
  463. // Returns the odd nodes in this dojo.NodeList as a dojo.NodeList.
  464. // description:
  465. // .end() can be used on the returned dojo.NodeList to get back to the
  466. // original dojo.NodeList.
  467. // returns:
  468. // dojo.NodeList, with the odd nodes in this dojo.NodeList
  469. // example:
  470. // assume a DOM created by this markup:
  471. // | <div class="container">
  472. // | <div class="interior red">Red One</div>
  473. // | <div class="interior blue">Blue One</div>
  474. // | <div class="interior red">Red Two</div>
  475. // | <div class="interior blue">Blue Two</div>
  476. // | </div>
  477. // Running this code:
  478. // | dojo.query(".interior").odd();
  479. // returns the two divs with class "red"
  480. return this.filter(function(item, i){
  481. return i % 2 == 0;
  482. }); //dojo.NodeList
  483. }
  484. });
  485. return NodeList;
  486. });