traversing.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831
  1. /**
  2. * Methods for traversing the DOM structure.
  3. *
  4. * @module cheerio/traversing
  5. */
  6. import { hasChildren, isDocument, } from 'domhandler';
  7. import * as select from 'cheerio-select';
  8. import { domEach, isTag, isCheerio } from '../utils.js';
  9. import { contains } from '../static.js';
  10. import { getChildren, getSiblings, nextElementSibling, prevElementSibling, uniqueSort, } from 'domutils';
  11. const reSiblingSelector = /^\s*[~+]/;
  12. /**
  13. * Get the descendants of each element in the current set of matched elements,
  14. * filtered by a selector, jQuery object, or element.
  15. *
  16. * @category Traversing
  17. * @example
  18. *
  19. * ```js
  20. * $('#fruits').find('li').length;
  21. * //=> 3
  22. * $('#fruits').find($('.apple')).length;
  23. * //=> 1
  24. * ```
  25. *
  26. * @param selectorOrHaystack - Element to look for.
  27. * @returns The found elements.
  28. * @see {@link https://api.jquery.com/find/}
  29. */
  30. export function find(selectorOrHaystack) {
  31. var _a;
  32. if (!selectorOrHaystack) {
  33. return this._make([]);
  34. }
  35. const context = this.toArray();
  36. if (typeof selectorOrHaystack !== 'string') {
  37. const haystack = isCheerio(selectorOrHaystack)
  38. ? selectorOrHaystack.toArray()
  39. : [selectorOrHaystack];
  40. return this._make(haystack.filter((elem) => context.some((node) => contains(node, elem))));
  41. }
  42. const elems = reSiblingSelector.test(selectorOrHaystack)
  43. ? context
  44. : this.children().toArray();
  45. const options = {
  46. context,
  47. root: (_a = this._root) === null || _a === void 0 ? void 0 : _a[0],
  48. // Pass options that are recognized by `cheerio-select`
  49. xmlMode: this.options.xmlMode,
  50. lowerCaseTags: this.options.lowerCaseTags,
  51. lowerCaseAttributeNames: this.options.lowerCaseAttributeNames,
  52. pseudos: this.options.pseudos,
  53. quirksMode: this.options.quirksMode,
  54. };
  55. return this._make(select.select(selectorOrHaystack, elems, options));
  56. }
  57. /**
  58. * Creates a matcher, using a particular mapping function. Matchers provide a
  59. * function that finds elements using a generating function, supporting filtering.
  60. *
  61. * @private
  62. * @param matchMap - Mapping function.
  63. * @returns - Function for wrapping generating functions.
  64. */
  65. function _getMatcher(matchMap) {
  66. return function (fn, ...postFns) {
  67. return function (selector) {
  68. var _a;
  69. let matched = matchMap(fn, this);
  70. if (selector) {
  71. matched = filterArray(matched, selector, this.options.xmlMode, (_a = this._root) === null || _a === void 0 ? void 0 : _a[0]);
  72. }
  73. return this._make(
  74. // Post processing is only necessary if there is more than one element.
  75. this.length > 1 && matched.length > 1
  76. ? postFns.reduce((elems, fn) => fn(elems), matched)
  77. : matched);
  78. };
  79. };
  80. }
  81. /** Matcher that adds multiple elements for each entry in the input. */
  82. const _matcher = _getMatcher((fn, elems) => {
  83. const ret = [];
  84. for (let i = 0; i < elems.length; i++) {
  85. const value = fn(elems[i]);
  86. ret.push(value);
  87. }
  88. return new Array().concat(...ret);
  89. });
  90. /** Matcher that adds at most one element for each entry in the input. */
  91. const _singleMatcher = _getMatcher((fn, elems) => {
  92. const ret = [];
  93. for (let i = 0; i < elems.length; i++) {
  94. const value = fn(elems[i]);
  95. if (value !== null) {
  96. ret.push(value);
  97. }
  98. }
  99. return ret;
  100. });
  101. /**
  102. * Matcher that supports traversing until a condition is met.
  103. *
  104. * @returns A function usable for `*Until` methods.
  105. */
  106. function _matchUntil(nextElem, ...postFns) {
  107. // We use a variable here that is used from within the matcher.
  108. let matches = null;
  109. const innerMatcher = _getMatcher((nextElem, elems) => {
  110. const matched = [];
  111. domEach(elems, (elem) => {
  112. for (let next; (next = nextElem(elem)); elem = next) {
  113. // FIXME: `matched` might contain duplicates here and the index is too large.
  114. if (matches === null || matches === void 0 ? void 0 : matches(next, matched.length))
  115. break;
  116. matched.push(next);
  117. }
  118. });
  119. return matched;
  120. })(nextElem, ...postFns);
  121. return function (selector, filterSelector) {
  122. // Override `matches` variable with the new target.
  123. matches =
  124. typeof selector === 'string'
  125. ? (elem) => select.is(elem, selector, this.options)
  126. : selector
  127. ? getFilterFn(selector)
  128. : null;
  129. const ret = innerMatcher.call(this, filterSelector);
  130. // Set `matches` to `null`, so we don't waste memory.
  131. matches = null;
  132. return ret;
  133. };
  134. }
  135. function _removeDuplicates(elems) {
  136. return Array.from(new Set(elems));
  137. }
  138. /**
  139. * Get the parent of each element in the current set of matched elements,
  140. * optionally filtered by a selector.
  141. *
  142. * @category Traversing
  143. * @example
  144. *
  145. * ```js
  146. * $('.pear').parent().attr('id');
  147. * //=> fruits
  148. * ```
  149. *
  150. * @param selector - If specified filter for parent.
  151. * @returns The parents.
  152. * @see {@link https://api.jquery.com/parent/}
  153. */
  154. export const parent = _singleMatcher(({ parent }) => (parent && !isDocument(parent) ? parent : null), _removeDuplicates);
  155. /**
  156. * Get a set of parents filtered by `selector` of each element in the current
  157. * set of match elements.
  158. *
  159. * @category Traversing
  160. * @example
  161. *
  162. * ```js
  163. * $('.orange').parents().length;
  164. * //=> 2
  165. * $('.orange').parents('#fruits').length;
  166. * //=> 1
  167. * ```
  168. *
  169. * @param selector - If specified filter for parents.
  170. * @returns The parents.
  171. * @see {@link https://api.jquery.com/parents/}
  172. */
  173. export const parents = _matcher((elem) => {
  174. const matched = [];
  175. while (elem.parent && !isDocument(elem.parent)) {
  176. matched.push(elem.parent);
  177. elem = elem.parent;
  178. }
  179. return matched;
  180. }, uniqueSort, (elems) => elems.reverse());
  181. /**
  182. * Get the ancestors of each element in the current set of matched elements, up
  183. * to but not including the element matched by the selector, DOM node, or cheerio object.
  184. *
  185. * @category Traversing
  186. * @example
  187. *
  188. * ```js
  189. * $('.orange').parentsUntil('#food').length;
  190. * //=> 1
  191. * ```
  192. *
  193. * @param selector - Selector for element to stop at.
  194. * @param filterSelector - Optional filter for parents.
  195. * @returns The parents.
  196. * @see {@link https://api.jquery.com/parentsUntil/}
  197. */
  198. export const parentsUntil = _matchUntil(({ parent }) => (parent && !isDocument(parent) ? parent : null), uniqueSort, (elems) => elems.reverse());
  199. /**
  200. * For each element in the set, get the first element that matches the selector
  201. * by testing the element itself and traversing up through its ancestors in the DOM tree.
  202. *
  203. * @category Traversing
  204. * @example
  205. *
  206. * ```js
  207. * $('.orange').closest();
  208. * //=> []
  209. *
  210. * $('.orange').closest('.apple');
  211. * // => []
  212. *
  213. * $('.orange').closest('li');
  214. * //=> [<li class="orange">Orange</li>]
  215. *
  216. * $('.orange').closest('#fruits');
  217. * //=> [<ul id="fruits"> ... </ul>]
  218. * ```
  219. *
  220. * @param selector - Selector for the element to find.
  221. * @returns The closest nodes.
  222. * @see {@link https://api.jquery.com/closest/}
  223. */
  224. export function closest(selector) {
  225. var _a;
  226. const set = [];
  227. if (!selector) {
  228. return this._make(set);
  229. }
  230. const selectOpts = {
  231. xmlMode: this.options.xmlMode,
  232. root: (_a = this._root) === null || _a === void 0 ? void 0 : _a[0],
  233. };
  234. const selectFn = typeof selector === 'string'
  235. ? (elem) => select.is(elem, selector, selectOpts)
  236. : getFilterFn(selector);
  237. domEach(this, (elem) => {
  238. while (elem && isTag(elem)) {
  239. if (selectFn(elem, 0)) {
  240. // Do not add duplicate elements to the set
  241. if (!set.includes(elem)) {
  242. set.push(elem);
  243. }
  244. break;
  245. }
  246. elem = elem.parent;
  247. }
  248. });
  249. return this._make(set);
  250. }
  251. /**
  252. * Gets the next sibling of the first selected element, optionally filtered by a selector.
  253. *
  254. * @category Traversing
  255. * @example
  256. *
  257. * ```js
  258. * $('.apple').next().hasClass('orange');
  259. * //=> true
  260. * ```
  261. *
  262. * @param selector - If specified filter for sibling.
  263. * @returns The next nodes.
  264. * @see {@link https://api.jquery.com/next/}
  265. */
  266. export const next = _singleMatcher((elem) => nextElementSibling(elem));
  267. /**
  268. * Gets all the following siblings of the first selected element, optionally
  269. * filtered by a selector.
  270. *
  271. * @category Traversing
  272. * @example
  273. *
  274. * ```js
  275. * $('.apple').nextAll();
  276. * //=> [<li class="orange">Orange</li>, <li class="pear">Pear</li>]
  277. * $('.apple').nextAll('.orange');
  278. * //=> [<li class="orange">Orange</li>]
  279. * ```
  280. *
  281. * @param selector - If specified filter for siblings.
  282. * @returns The next nodes.
  283. * @see {@link https://api.jquery.com/nextAll/}
  284. */
  285. export const nextAll = _matcher((elem) => {
  286. const matched = [];
  287. while (elem.next) {
  288. elem = elem.next;
  289. if (isTag(elem))
  290. matched.push(elem);
  291. }
  292. return matched;
  293. }, _removeDuplicates);
  294. /**
  295. * Gets all the following siblings up to but not including the element matched
  296. * by the selector, optionally filtered by another selector.
  297. *
  298. * @category Traversing
  299. * @example
  300. *
  301. * ```js
  302. * $('.apple').nextUntil('.pear');
  303. * //=> [<li class="orange">Orange</li>]
  304. * ```
  305. *
  306. * @param selector - Selector for element to stop at.
  307. * @param filterSelector - If specified filter for siblings.
  308. * @returns The next nodes.
  309. * @see {@link https://api.jquery.com/nextUntil/}
  310. */
  311. export const nextUntil = _matchUntil((el) => nextElementSibling(el), _removeDuplicates);
  312. /**
  313. * Gets the previous sibling of the first selected element optionally filtered
  314. * by a selector.
  315. *
  316. * @category Traversing
  317. * @example
  318. *
  319. * ```js
  320. * $('.orange').prev().hasClass('apple');
  321. * //=> true
  322. * ```
  323. *
  324. * @param selector - If specified filter for siblings.
  325. * @returns The previous nodes.
  326. * @see {@link https://api.jquery.com/prev/}
  327. */
  328. export const prev = _singleMatcher((elem) => prevElementSibling(elem));
  329. /**
  330. * Gets all the preceding siblings of the first selected element, optionally
  331. * filtered by a selector.
  332. *
  333. * @category Traversing
  334. * @example
  335. *
  336. * ```js
  337. * $('.pear').prevAll();
  338. * //=> [<li class="orange">Orange</li>, <li class="apple">Apple</li>]
  339. *
  340. * $('.pear').prevAll('.orange');
  341. * //=> [<li class="orange">Orange</li>]
  342. * ```
  343. *
  344. * @param selector - If specified filter for siblings.
  345. * @returns The previous nodes.
  346. * @see {@link https://api.jquery.com/prevAll/}
  347. */
  348. export const prevAll = _matcher((elem) => {
  349. const matched = [];
  350. while (elem.prev) {
  351. elem = elem.prev;
  352. if (isTag(elem))
  353. matched.push(elem);
  354. }
  355. return matched;
  356. }, _removeDuplicates);
  357. /**
  358. * Gets all the preceding siblings up to but not including the element matched
  359. * by the selector, optionally filtered by another selector.
  360. *
  361. * @category Traversing
  362. * @example
  363. *
  364. * ```js
  365. * $('.pear').prevUntil('.apple');
  366. * //=> [<li class="orange">Orange</li>]
  367. * ```
  368. *
  369. * @param selector - Selector for element to stop at.
  370. * @param filterSelector - If specified filter for siblings.
  371. * @returns The previous nodes.
  372. * @see {@link https://api.jquery.com/prevUntil/}
  373. */
  374. export const prevUntil = _matchUntil((el) => prevElementSibling(el), _removeDuplicates);
  375. /**
  376. * Get the siblings of each element (excluding the element) in the set of
  377. * matched elements, optionally filtered by a selector.
  378. *
  379. * @category Traversing
  380. * @example
  381. *
  382. * ```js
  383. * $('.pear').siblings().length;
  384. * //=> 2
  385. *
  386. * $('.pear').siblings('.orange').length;
  387. * //=> 1
  388. * ```
  389. *
  390. * @param selector - If specified filter for siblings.
  391. * @returns The siblings.
  392. * @see {@link https://api.jquery.com/siblings/}
  393. */
  394. export const siblings = _matcher((elem) => getSiblings(elem).filter((el) => isTag(el) && el !== elem), uniqueSort);
  395. /**
  396. * Gets the element children of each element in the set of matched elements.
  397. *
  398. * @category Traversing
  399. * @example
  400. *
  401. * ```js
  402. * $('#fruits').children().length;
  403. * //=> 3
  404. *
  405. * $('#fruits').children('.pear').text();
  406. * //=> Pear
  407. * ```
  408. *
  409. * @param selector - If specified filter for children.
  410. * @returns The children.
  411. * @see {@link https://api.jquery.com/children/}
  412. */
  413. export const children = _matcher((elem) => getChildren(elem).filter(isTag), _removeDuplicates);
  414. /**
  415. * Gets the children of each element in the set of matched elements, including
  416. * text and comment nodes.
  417. *
  418. * @category Traversing
  419. * @example
  420. *
  421. * ```js
  422. * $('#fruits').contents().length;
  423. * //=> 3
  424. * ```
  425. *
  426. * @returns The children.
  427. * @see {@link https://api.jquery.com/contents/}
  428. */
  429. export function contents() {
  430. const elems = this.toArray().reduce((newElems, elem) => hasChildren(elem) ? newElems.concat(elem.children) : newElems, []);
  431. return this._make(elems);
  432. }
  433. /**
  434. * Iterates over a cheerio object, executing a function for each matched
  435. * element. When the callback is fired, the function is fired in the context of
  436. * the DOM element, so `this` refers to the current element, which is equivalent
  437. * to the function parameter `element`. To break out of the `each` loop early,
  438. * return with `false`.
  439. *
  440. * @category Traversing
  441. * @example
  442. *
  443. * ```js
  444. * const fruits = [];
  445. *
  446. * $('li').each(function (i, elem) {
  447. * fruits[i] = $(this).text();
  448. * });
  449. *
  450. * fruits.join(', ');
  451. * //=> Apple, Orange, Pear
  452. * ```
  453. *
  454. * @param fn - Function to execute.
  455. * @returns The instance itself, useful for chaining.
  456. * @see {@link https://api.jquery.com/each/}
  457. */
  458. export function each(fn) {
  459. let i = 0;
  460. const len = this.length;
  461. while (i < len && fn.call(this[i], i, this[i]) !== false)
  462. ++i;
  463. return this;
  464. }
  465. /**
  466. * Pass each element in the current matched set through a function, producing a
  467. * new Cheerio object containing the return values. The function can return an
  468. * individual data item or an array of data items to be inserted into the
  469. * resulting set. If an array is returned, the elements inside the array are
  470. * inserted into the set. If the function returns null or undefined, no element
  471. * will be inserted.
  472. *
  473. * @category Traversing
  474. * @example
  475. *
  476. * ```js
  477. * $('li')
  478. * .map(function (i, el) {
  479. * // this === el
  480. * return $(this).text();
  481. * })
  482. * .toArray()
  483. * .join(' ');
  484. * //=> "apple orange pear"
  485. * ```
  486. *
  487. * @param fn - Function to execute.
  488. * @returns The mapped elements, wrapped in a Cheerio collection.
  489. * @see {@link https://api.jquery.com/map/}
  490. */
  491. export function map(fn) {
  492. let elems = [];
  493. for (let i = 0; i < this.length; i++) {
  494. const el = this[i];
  495. const val = fn.call(el, i, el);
  496. if (val != null) {
  497. elems = elems.concat(val);
  498. }
  499. }
  500. return this._make(elems);
  501. }
  502. /**
  503. * Creates a function to test if a filter is matched.
  504. *
  505. * @param match - A filter.
  506. * @returns A function that determines if a filter has been matched.
  507. */
  508. function getFilterFn(match) {
  509. if (typeof match === 'function') {
  510. return (el, i) => match.call(el, i, el);
  511. }
  512. if (isCheerio(match)) {
  513. return (el) => Array.prototype.includes.call(match, el);
  514. }
  515. return function (el) {
  516. return match === el;
  517. };
  518. }
  519. export function filter(match) {
  520. var _a;
  521. return this._make(filterArray(this.toArray(), match, this.options.xmlMode, (_a = this._root) === null || _a === void 0 ? void 0 : _a[0]));
  522. }
  523. export function filterArray(nodes, match, xmlMode, root) {
  524. return typeof match === 'string'
  525. ? select.filter(match, nodes, { xmlMode, root })
  526. : nodes.filter(getFilterFn(match));
  527. }
  528. /**
  529. * Checks the current list of elements and returns `true` if _any_ of the
  530. * elements match the selector. If using an element or Cheerio selection,
  531. * returns `true` if _any_ of the elements match. If using a predicate function,
  532. * the function is executed in the context of the selected element, so `this`
  533. * refers to the current element.
  534. *
  535. * @category Attributes
  536. * @param selector - Selector for the selection.
  537. * @returns Whether or not the selector matches an element of the instance.
  538. * @see {@link https://api.jquery.com/is/}
  539. */
  540. export function is(selector) {
  541. const nodes = this.toArray();
  542. return typeof selector === 'string'
  543. ? select.some(nodes.filter(isTag), selector, this.options)
  544. : selector
  545. ? nodes.some(getFilterFn(selector))
  546. : false;
  547. }
  548. /**
  549. * Remove elements from the set of matched elements. Given a Cheerio object that
  550. * represents a set of DOM elements, the `.not()` method constructs a new
  551. * Cheerio object from a subset of the matching elements. The supplied selector
  552. * is tested against each element; the elements that don't match the selector
  553. * will be included in the result.
  554. *
  555. * The `.not()` method can take a function as its argument in the same way that
  556. * `.filter()` does. Elements for which the function returns `true` are excluded
  557. * from the filtered set; all other elements are included.
  558. *
  559. * @category Traversing
  560. * @example <caption>Selector</caption>
  561. *
  562. * ```js
  563. * $('li').not('.apple').length;
  564. * //=> 2
  565. * ```
  566. *
  567. * @example <caption>Function</caption>
  568. *
  569. * ```js
  570. * $('li').not(function (i, el) {
  571. * // this === el
  572. * return $(this).attr('class') === 'orange';
  573. * }).length; //=> 2
  574. * ```
  575. *
  576. * @param match - Value to look for, following the rules above.
  577. * @param container - Optional node to filter instead.
  578. * @returns The filtered collection.
  579. * @see {@link https://api.jquery.com/not/}
  580. */
  581. export function not(match) {
  582. let nodes = this.toArray();
  583. if (typeof match === 'string') {
  584. const matches = new Set(select.filter(match, nodes, this.options));
  585. nodes = nodes.filter((el) => !matches.has(el));
  586. }
  587. else {
  588. const filterFn = getFilterFn(match);
  589. nodes = nodes.filter((el, i) => !filterFn(el, i));
  590. }
  591. return this._make(nodes);
  592. }
  593. /**
  594. * Filters the set of matched elements to only those which have the given DOM
  595. * element as a descendant or which have a descendant that matches the given
  596. * selector. Equivalent to `.filter(':has(selector)')`.
  597. *
  598. * @category Traversing
  599. * @example <caption>Selector</caption>
  600. *
  601. * ```js
  602. * $('ul').has('.pear').attr('id');
  603. * //=> fruits
  604. * ```
  605. *
  606. * @example <caption>Element</caption>
  607. *
  608. * ```js
  609. * $('ul').has($('.pear')[0]).attr('id');
  610. * //=> fruits
  611. * ```
  612. *
  613. * @param selectorOrHaystack - Element to look for.
  614. * @returns The filtered collection.
  615. * @see {@link https://api.jquery.com/has/}
  616. */
  617. export function has(selectorOrHaystack) {
  618. return this.filter(typeof selectorOrHaystack === 'string'
  619. ? // Using the `:has` selector here short-circuits searches.
  620. `:has(${selectorOrHaystack})`
  621. : (_, el) => this._make(el).find(selectorOrHaystack).length > 0);
  622. }
  623. /**
  624. * Will select the first element of a cheerio object.
  625. *
  626. * @category Traversing
  627. * @example
  628. *
  629. * ```js
  630. * $('#fruits').children().first().text();
  631. * //=> Apple
  632. * ```
  633. *
  634. * @returns The first element.
  635. * @see {@link https://api.jquery.com/first/}
  636. */
  637. export function first() {
  638. return this.length > 1 ? this._make(this[0]) : this;
  639. }
  640. /**
  641. * Will select the last element of a cheerio object.
  642. *
  643. * @category Traversing
  644. * @example
  645. *
  646. * ```js
  647. * $('#fruits').children().last().text();
  648. * //=> Pear
  649. * ```
  650. *
  651. * @returns The last element.
  652. * @see {@link https://api.jquery.com/last/}
  653. */
  654. export function last() {
  655. return this.length > 0 ? this._make(this[this.length - 1]) : this;
  656. }
  657. /**
  658. * Reduce the set of matched elements to the one at the specified index. Use
  659. * `.eq(-i)` to count backwards from the last selected element.
  660. *
  661. * @category Traversing
  662. * @example
  663. *
  664. * ```js
  665. * $('li').eq(0).text();
  666. * //=> Apple
  667. *
  668. * $('li').eq(-1).text();
  669. * //=> Pear
  670. * ```
  671. *
  672. * @param i - Index of the element to select.
  673. * @returns The element at the `i`th position.
  674. * @see {@link https://api.jquery.com/eq/}
  675. */
  676. export function eq(i) {
  677. var _a;
  678. i = +i;
  679. // Use the first identity optimization if possible
  680. if (i === 0 && this.length <= 1)
  681. return this;
  682. if (i < 0)
  683. i = this.length + i;
  684. return this._make((_a = this[i]) !== null && _a !== void 0 ? _a : []);
  685. }
  686. export function get(i) {
  687. if (i == null) {
  688. return this.toArray();
  689. }
  690. return this[i < 0 ? this.length + i : i];
  691. }
  692. /**
  693. * Retrieve all the DOM elements contained in the jQuery set as an array.
  694. *
  695. * @example
  696. *
  697. * ```js
  698. * $('li').toArray();
  699. * //=> [ {...}, {...}, {...} ]
  700. * ```
  701. *
  702. * @returns The contained items.
  703. */
  704. export function toArray() {
  705. return Array.prototype.slice.call(this);
  706. }
  707. /**
  708. * Search for a given element from among the matched elements.
  709. *
  710. * @category Traversing
  711. * @example
  712. *
  713. * ```js
  714. * $('.pear').index();
  715. * //=> 2 $('.orange').index('li');
  716. * //=> 1
  717. * $('.apple').index($('#fruit, li'));
  718. * //=> 1
  719. * ```
  720. *
  721. * @param selectorOrNeedle - Element to look for.
  722. * @returns The index of the element.
  723. * @see {@link https://api.jquery.com/index/}
  724. */
  725. export function index(selectorOrNeedle) {
  726. let $haystack;
  727. let needle;
  728. if (selectorOrNeedle == null) {
  729. $haystack = this.parent().children();
  730. needle = this[0];
  731. }
  732. else if (typeof selectorOrNeedle === 'string') {
  733. $haystack = this._make(selectorOrNeedle);
  734. needle = this[0];
  735. }
  736. else {
  737. // eslint-disable-next-line @typescript-eslint/no-this-alias
  738. $haystack = this;
  739. needle = isCheerio(selectorOrNeedle)
  740. ? selectorOrNeedle[0]
  741. : selectorOrNeedle;
  742. }
  743. return Array.prototype.indexOf.call($haystack, needle);
  744. }
  745. /**
  746. * Gets the elements matching the specified range (0-based position).
  747. *
  748. * @category Traversing
  749. * @example
  750. *
  751. * ```js
  752. * $('li').slice(1).eq(0).text();
  753. * //=> 'Orange'
  754. *
  755. * $('li').slice(1, 2).length;
  756. * //=> 1
  757. * ```
  758. *
  759. * @param start - A position at which the elements begin to be selected. If
  760. * negative, it indicates an offset from the end of the set.
  761. * @param end - A position at which the elements stop being selected. If
  762. * negative, it indicates an offset from the end of the set. If omitted, the
  763. * range continues until the end of the set.
  764. * @returns The elements matching the specified range.
  765. * @see {@link https://api.jquery.com/slice/}
  766. */
  767. export function slice(start, end) {
  768. return this._make(Array.prototype.slice.call(this, start, end));
  769. }
  770. /**
  771. * End the most recent filtering operation in the current chain and return the
  772. * set of matched elements to its previous state.
  773. *
  774. * @category Traversing
  775. * @example
  776. *
  777. * ```js
  778. * $('li').eq(0).end().length;
  779. * //=> 3
  780. * ```
  781. *
  782. * @returns The previous state of the set of matched elements.
  783. * @see {@link https://api.jquery.com/end/}
  784. */
  785. export function end() {
  786. var _a;
  787. return (_a = this.prevObject) !== null && _a !== void 0 ? _a : this._make([]);
  788. }
  789. /**
  790. * Add elements to the set of matched elements.
  791. *
  792. * @category Traversing
  793. * @example
  794. *
  795. * ```js
  796. * $('.apple').add('.orange').length;
  797. * //=> 2
  798. * ```
  799. *
  800. * @param other - Elements to add.
  801. * @param context - Optionally the context of the new selection.
  802. * @returns The combined set.
  803. * @see {@link https://api.jquery.com/add/}
  804. */
  805. export function add(other, context) {
  806. const selection = this._make(other, context);
  807. const contents = uniqueSort([...this.get(), ...selection.get()]);
  808. return this._make(contents);
  809. }
  810. /**
  811. * Add the previous set of elements on the stack to the current set, optionally
  812. * filtered by a selector.
  813. *
  814. * @category Traversing
  815. * @example
  816. *
  817. * ```js
  818. * $('li').eq(0).addBack('.orange').length;
  819. * //=> 2
  820. * ```
  821. *
  822. * @param selector - Selector for the elements to add.
  823. * @returns The combined set.
  824. * @see {@link https://api.jquery.com/addBack/}
  825. */
  826. export function addBack(selector) {
  827. return this.prevObject
  828. ? this.add(selector ? this.prevObject.filter(selector) : this.prevObject)
  829. : this;
  830. }
  831. //# sourceMappingURL=traversing.js.map