manipulation.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850
  1. "use strict";
  2. /**
  3. * Methods for modifying the DOM structure.
  4. *
  5. * @module cheerio/manipulation
  6. */
  7. Object.defineProperty(exports, "__esModule", { value: true });
  8. exports.wrapInner = exports.wrap = exports.prepend = exports.append = void 0;
  9. exports._makeDomArray = _makeDomArray;
  10. exports.appendTo = appendTo;
  11. exports.prependTo = prependTo;
  12. exports.unwrap = unwrap;
  13. exports.wrapAll = wrapAll;
  14. exports.after = after;
  15. exports.insertAfter = insertAfter;
  16. exports.before = before;
  17. exports.insertBefore = insertBefore;
  18. exports.remove = remove;
  19. exports.replaceWith = replaceWith;
  20. exports.empty = empty;
  21. exports.html = html;
  22. exports.toString = toString;
  23. exports.text = text;
  24. exports.clone = clone;
  25. const domhandler_1 = require("domhandler");
  26. const parse_js_1 = require("../parse.js");
  27. const static_js_1 = require("../static.js");
  28. const utils_js_1 = require("../utils.js");
  29. const domutils_1 = require("domutils");
  30. const htmlparser2_1 = require("htmlparser2");
  31. /**
  32. * Create an array of nodes, recursing into arrays and parsing strings if
  33. * necessary.
  34. *
  35. * @private
  36. * @category Manipulation
  37. * @param elem - Elements to make an array of.
  38. * @param clone - Optionally clone nodes.
  39. * @returns The array of nodes.
  40. */
  41. function _makeDomArray(elem, clone) {
  42. if (elem == null) {
  43. return [];
  44. }
  45. if (typeof elem === 'string') {
  46. return this._parse(elem, this.options, false, null).children.slice(0);
  47. }
  48. if ('length' in elem) {
  49. if (elem.length === 1) {
  50. return this._makeDomArray(elem[0], clone);
  51. }
  52. const result = [];
  53. for (let i = 0; i < elem.length; i++) {
  54. const el = elem[i];
  55. if (typeof el === 'object') {
  56. if (el == null) {
  57. continue;
  58. }
  59. if (!('length' in el)) {
  60. result.push(clone ? (0, domhandler_1.cloneNode)(el, true) : el);
  61. continue;
  62. }
  63. }
  64. result.push(...this._makeDomArray(el, clone));
  65. }
  66. return result;
  67. }
  68. return [clone ? (0, domhandler_1.cloneNode)(elem, true) : elem];
  69. }
  70. function _insert(concatenator) {
  71. return function (...elems) {
  72. const lastIdx = this.length - 1;
  73. return (0, utils_js_1.domEach)(this, (el, i) => {
  74. if (!(0, domhandler_1.hasChildren)(el))
  75. return;
  76. const domSrc = typeof elems[0] === 'function'
  77. ? elems[0].call(el, i, this._render(el.children))
  78. : elems;
  79. const dom = this._makeDomArray(domSrc, i < lastIdx);
  80. concatenator(dom, el.children, el);
  81. });
  82. };
  83. }
  84. /**
  85. * Modify an array in-place, removing some number of elements and adding new
  86. * elements directly following them.
  87. *
  88. * @private
  89. * @category Manipulation
  90. * @param array - Target array to splice.
  91. * @param spliceIdx - Index at which to begin changing the array.
  92. * @param spliceCount - Number of elements to remove from the array.
  93. * @param newElems - Elements to insert into the array.
  94. * @param parent - The parent of the node.
  95. * @returns The spliced array.
  96. */
  97. function uniqueSplice(array, spliceIdx, spliceCount, newElems, parent) {
  98. var _a, _b;
  99. const spliceArgs = [
  100. spliceIdx,
  101. spliceCount,
  102. ...newElems,
  103. ];
  104. const prev = spliceIdx === 0 ? null : array[spliceIdx - 1];
  105. const next = spliceIdx + spliceCount >= array.length
  106. ? null
  107. : array[spliceIdx + spliceCount];
  108. /*
  109. * Before splicing in new elements, ensure they do not already appear in the
  110. * current array.
  111. */
  112. for (let idx = 0; idx < newElems.length; ++idx) {
  113. const node = newElems[idx];
  114. const oldParent = node.parent;
  115. if (oldParent) {
  116. const oldSiblings = oldParent.children;
  117. const prevIdx = oldSiblings.indexOf(node);
  118. if (prevIdx !== -1) {
  119. oldParent.children.splice(prevIdx, 1);
  120. if (parent === oldParent && spliceIdx > prevIdx) {
  121. spliceArgs[0]--;
  122. }
  123. }
  124. }
  125. node.parent = parent;
  126. if (node.prev) {
  127. node.prev.next = (_a = node.next) !== null && _a !== void 0 ? _a : null;
  128. }
  129. if (node.next) {
  130. node.next.prev = (_b = node.prev) !== null && _b !== void 0 ? _b : null;
  131. }
  132. node.prev = idx === 0 ? prev : newElems[idx - 1];
  133. node.next = idx === newElems.length - 1 ? next : newElems[idx + 1];
  134. }
  135. if (prev) {
  136. prev.next = newElems[0];
  137. }
  138. if (next) {
  139. next.prev = newElems[newElems.length - 1];
  140. }
  141. return array.splice(...spliceArgs);
  142. }
  143. /**
  144. * Insert every element in the set of matched elements to the end of the target.
  145. *
  146. * @category Manipulation
  147. * @example
  148. *
  149. * ```js
  150. * $('<li class="plum">Plum</li>').appendTo('#fruits');
  151. * $.html();
  152. * //=> <ul id="fruits">
  153. * // <li class="apple">Apple</li>
  154. * // <li class="orange">Orange</li>
  155. * // <li class="pear">Pear</li>
  156. * // <li class="plum">Plum</li>
  157. * // </ul>
  158. * ```
  159. *
  160. * @param target - Element to append elements to.
  161. * @returns The instance itself.
  162. * @see {@link https://api.jquery.com/appendTo/}
  163. */
  164. function appendTo(target) {
  165. const appendTarget = (0, utils_js_1.isCheerio)(target) ? target : this._make(target);
  166. appendTarget.append(this);
  167. return this;
  168. }
  169. /**
  170. * Insert every element in the set of matched elements to the beginning of the
  171. * target.
  172. *
  173. * @category Manipulation
  174. * @example
  175. *
  176. * ```js
  177. * $('<li class="plum">Plum</li>').prependTo('#fruits');
  178. * $.html();
  179. * //=> <ul id="fruits">
  180. * // <li class="plum">Plum</li>
  181. * // <li class="apple">Apple</li>
  182. * // <li class="orange">Orange</li>
  183. * // <li class="pear">Pear</li>
  184. * // </ul>
  185. * ```
  186. *
  187. * @param target - Element to prepend elements to.
  188. * @returns The instance itself.
  189. * @see {@link https://api.jquery.com/prependTo/}
  190. */
  191. function prependTo(target) {
  192. const prependTarget = (0, utils_js_1.isCheerio)(target) ? target : this._make(target);
  193. prependTarget.prepend(this);
  194. return this;
  195. }
  196. /**
  197. * Inserts content as the _last_ child of each of the selected elements.
  198. *
  199. * @category Manipulation
  200. * @example
  201. *
  202. * ```js
  203. * $('ul').append('<li class="plum">Plum</li>');
  204. * $.html();
  205. * //=> <ul id="fruits">
  206. * // <li class="apple">Apple</li>
  207. * // <li class="orange">Orange</li>
  208. * // <li class="pear">Pear</li>
  209. * // <li class="plum">Plum</li>
  210. * // </ul>
  211. * ```
  212. *
  213. * @see {@link https://api.jquery.com/append/}
  214. */
  215. exports.append = _insert((dom, children, parent) => {
  216. uniqueSplice(children, children.length, 0, dom, parent);
  217. });
  218. /**
  219. * Inserts content as the _first_ child of each of the selected elements.
  220. *
  221. * @category Manipulation
  222. * @example
  223. *
  224. * ```js
  225. * $('ul').prepend('<li class="plum">Plum</li>');
  226. * $.html();
  227. * //=> <ul id="fruits">
  228. * // <li class="plum">Plum</li>
  229. * // <li class="apple">Apple</li>
  230. * // <li class="orange">Orange</li>
  231. * // <li class="pear">Pear</li>
  232. * // </ul>
  233. * ```
  234. *
  235. * @see {@link https://api.jquery.com/prepend/}
  236. */
  237. exports.prepend = _insert((dom, children, parent) => {
  238. uniqueSplice(children, 0, 0, dom, parent);
  239. });
  240. function _wrap(insert) {
  241. return function (wrapper) {
  242. const lastIdx = this.length - 1;
  243. const lastParent = this.parents().last();
  244. for (let i = 0; i < this.length; i++) {
  245. const el = this[i];
  246. const wrap = typeof wrapper === 'function'
  247. ? wrapper.call(el, i, el)
  248. : typeof wrapper === 'string' && !(0, utils_js_1.isHtml)(wrapper)
  249. ? lastParent.find(wrapper).clone()
  250. : wrapper;
  251. const [wrapperDom] = this._makeDomArray(wrap, i < lastIdx);
  252. if (!wrapperDom || !(0, domhandler_1.hasChildren)(wrapperDom))
  253. continue;
  254. let elInsertLocation = wrapperDom;
  255. /*
  256. * Find the deepest child. Only consider the first tag child of each node
  257. * (ignore text); stop if no children are found.
  258. */
  259. let j = 0;
  260. while (j < elInsertLocation.children.length) {
  261. const child = elInsertLocation.children[j];
  262. if ((0, domhandler_1.isTag)(child)) {
  263. elInsertLocation = child;
  264. j = 0;
  265. }
  266. else {
  267. j++;
  268. }
  269. }
  270. insert(el, elInsertLocation, [wrapperDom]);
  271. }
  272. return this;
  273. };
  274. }
  275. /**
  276. * The .wrap() function can take any string or object that could be passed to
  277. * the $() factory function to specify a DOM structure. This structure may be
  278. * nested several levels deep, but should contain only one inmost element. A
  279. * copy of this structure will be wrapped around each of the elements in the set
  280. * of matched elements. This method returns the original set of elements for
  281. * chaining purposes.
  282. *
  283. * @category Manipulation
  284. * @example
  285. *
  286. * ```js
  287. * const redFruit = $('<div class="red-fruit"></div>');
  288. * $('.apple').wrap(redFruit);
  289. *
  290. * //=> <ul id="fruits">
  291. * // <div class="red-fruit">
  292. * // <li class="apple">Apple</li>
  293. * // </div>
  294. * // <li class="orange">Orange</li>
  295. * // <li class="plum">Plum</li>
  296. * // </ul>
  297. *
  298. * const healthy = $('<div class="healthy"></div>');
  299. * $('li').wrap(healthy);
  300. *
  301. * //=> <ul id="fruits">
  302. * // <div class="healthy">
  303. * // <li class="apple">Apple</li>
  304. * // </div>
  305. * // <div class="healthy">
  306. * // <li class="orange">Orange</li>
  307. * // </div>
  308. * // <div class="healthy">
  309. * // <li class="plum">Plum</li>
  310. * // </div>
  311. * // </ul>
  312. * ```
  313. *
  314. * @param wrapper - The DOM structure to wrap around each element in the
  315. * selection.
  316. * @see {@link https://api.jquery.com/wrap/}
  317. */
  318. exports.wrap = _wrap((el, elInsertLocation, wrapperDom) => {
  319. const { parent } = el;
  320. if (!parent)
  321. return;
  322. const siblings = parent.children;
  323. const index = siblings.indexOf(el);
  324. (0, parse_js_1.update)([el], elInsertLocation);
  325. /*
  326. * The previous operation removed the current element from the `siblings`
  327. * array, so the `dom` array can be inserted without removing any
  328. * additional elements.
  329. */
  330. uniqueSplice(siblings, index, 0, wrapperDom, parent);
  331. });
  332. /**
  333. * The .wrapInner() function can take any string or object that could be passed
  334. * to the $() factory function to specify a DOM structure. This structure may be
  335. * nested several levels deep, but should contain only one inmost element. The
  336. * structure will be wrapped around the content of each of the elements in the
  337. * set of matched elements.
  338. *
  339. * @category Manipulation
  340. * @example
  341. *
  342. * ```js
  343. * const redFruit = $('<div class="red-fruit"></div>');
  344. * $('.apple').wrapInner(redFruit);
  345. *
  346. * //=> <ul id="fruits">
  347. * // <li class="apple">
  348. * // <div class="red-fruit">Apple</div>
  349. * // </li>
  350. * // <li class="orange">Orange</li>
  351. * // <li class="pear">Pear</li>
  352. * // </ul>
  353. *
  354. * const healthy = $('<div class="healthy"></div>');
  355. * $('li').wrapInner(healthy);
  356. *
  357. * //=> <ul id="fruits">
  358. * // <li class="apple">
  359. * // <div class="healthy">Apple</div>
  360. * // </li>
  361. * // <li class="orange">
  362. * // <div class="healthy">Orange</div>
  363. * // </li>
  364. * // <li class="pear">
  365. * // <div class="healthy">Pear</div>
  366. * // </li>
  367. * // </ul>
  368. * ```
  369. *
  370. * @param wrapper - The DOM structure to wrap around the content of each element
  371. * in the selection.
  372. * @returns The instance itself, for chaining.
  373. * @see {@link https://api.jquery.com/wrapInner/}
  374. */
  375. exports.wrapInner = _wrap((el, elInsertLocation, wrapperDom) => {
  376. if (!(0, domhandler_1.hasChildren)(el))
  377. return;
  378. (0, parse_js_1.update)(el.children, elInsertLocation);
  379. (0, parse_js_1.update)(wrapperDom, el);
  380. });
  381. /**
  382. * The .unwrap() function, removes the parents of the set of matched elements
  383. * from the DOM, leaving the matched elements in their place.
  384. *
  385. * @category Manipulation
  386. * @example <caption>without selector</caption>
  387. *
  388. * ```js
  389. * const $ = cheerio.load(
  390. * '<div id=test>\n <div><p>Hello</p></div>\n <div><p>World</p></div>\n</div>',
  391. * );
  392. * $('#test p').unwrap();
  393. *
  394. * //=> <div id=test>
  395. * // <p>Hello</p>
  396. * // <p>World</p>
  397. * // </div>
  398. * ```
  399. *
  400. * @example <caption>with selector</caption>
  401. *
  402. * ```js
  403. * const $ = cheerio.load(
  404. * '<div id=test>\n <p>Hello</p>\n <b><p>World</p></b>\n</div>',
  405. * );
  406. * $('#test p').unwrap('b');
  407. *
  408. * //=> <div id=test>
  409. * // <p>Hello</p>
  410. * // <p>World</p>
  411. * // </div>
  412. * ```
  413. *
  414. * @param selector - A selector to check the parent element against. If an
  415. * element's parent does not match the selector, the element won't be
  416. * unwrapped.
  417. * @returns The instance itself, for chaining.
  418. * @see {@link https://api.jquery.com/unwrap/}
  419. */
  420. function unwrap(selector) {
  421. this.parent(selector)
  422. .not('body')
  423. .each((_, el) => {
  424. this._make(el).replaceWith(el.children);
  425. });
  426. return this;
  427. }
  428. /**
  429. * The .wrapAll() function can take any string or object that could be passed to
  430. * the $() function to specify a DOM structure. This structure may be nested
  431. * several levels deep, but should contain only one inmost element. The
  432. * structure will be wrapped around all of the elements in the set of matched
  433. * elements, as a single group.
  434. *
  435. * @category Manipulation
  436. * @example <caption>With markup passed to `wrapAll`</caption>
  437. *
  438. * ```js
  439. * const $ = cheerio.load(
  440. * '<div class="container"><div class="inner">First</div><div class="inner">Second</div></div>',
  441. * );
  442. * $('.inner').wrapAll("<div class='new'></div>");
  443. *
  444. * //=> <div class="container">
  445. * // <div class='new'>
  446. * // <div class="inner">First</div>
  447. * // <div class="inner">Second</div>
  448. * // </div>
  449. * // </div>
  450. * ```
  451. *
  452. * @example <caption>With an existing cheerio instance</caption>
  453. *
  454. * ```js
  455. * const $ = cheerio.load(
  456. * '<span>Span 1</span><strong>Strong</strong><span>Span 2</span>',
  457. * );
  458. * const wrap = $('<div><p><em><b></b></em></p></div>');
  459. * $('span').wrapAll(wrap);
  460. *
  461. * //=> <div>
  462. * // <p>
  463. * // <em>
  464. * // <b>
  465. * // <span>Span 1</span>
  466. * // <span>Span 2</span>
  467. * // </b>
  468. * // </em>
  469. * // </p>
  470. * // </div>
  471. * // <strong>Strong</strong>
  472. * ```
  473. *
  474. * @param wrapper - The DOM structure to wrap around all matched elements in the
  475. * selection.
  476. * @returns The instance itself.
  477. * @see {@link https://api.jquery.com/wrapAll/}
  478. */
  479. function wrapAll(wrapper) {
  480. const el = this[0];
  481. if (el) {
  482. const wrap = this._make(typeof wrapper === 'function' ? wrapper.call(el, 0, el) : wrapper).insertBefore(el);
  483. // If html is given as wrapper, wrap may contain text elements
  484. let elInsertLocation;
  485. for (let i = 0; i < wrap.length; i++) {
  486. if (wrap[i].type === htmlparser2_1.ElementType.Tag) {
  487. elInsertLocation = wrap[i];
  488. }
  489. }
  490. let j = 0;
  491. /*
  492. * Find the deepest child. Only consider the first tag child of each node
  493. * (ignore text); stop if no children are found.
  494. */
  495. while (elInsertLocation && j < elInsertLocation.children.length) {
  496. const child = elInsertLocation.children[j];
  497. if (child.type === htmlparser2_1.ElementType.Tag) {
  498. elInsertLocation = child;
  499. j = 0;
  500. }
  501. else {
  502. j++;
  503. }
  504. }
  505. if (elInsertLocation)
  506. this._make(elInsertLocation).append(this);
  507. }
  508. return this;
  509. }
  510. /**
  511. * Insert content next to each element in the set of matched elements.
  512. *
  513. * @category Manipulation
  514. * @example
  515. *
  516. * ```js
  517. * $('.apple').after('<li class="plum">Plum</li>');
  518. * $.html();
  519. * //=> <ul id="fruits">
  520. * // <li class="apple">Apple</li>
  521. * // <li class="plum">Plum</li>
  522. * // <li class="orange">Orange</li>
  523. * // <li class="pear">Pear</li>
  524. * // </ul>
  525. * ```
  526. *
  527. * @param elems - HTML string, DOM element, array of DOM elements or Cheerio to
  528. * insert after each element in the set of matched elements.
  529. * @returns The instance itself.
  530. * @see {@link https://api.jquery.com/after/}
  531. */
  532. function after(...elems) {
  533. const lastIdx = this.length - 1;
  534. return (0, utils_js_1.domEach)(this, (el, i) => {
  535. if (!(0, domhandler_1.hasChildren)(el) || !el.parent) {
  536. return;
  537. }
  538. const siblings = el.parent.children;
  539. const index = siblings.indexOf(el);
  540. // If not found, move on
  541. /* istanbul ignore next */
  542. if (index === -1)
  543. return;
  544. const domSrc = typeof elems[0] === 'function'
  545. ? elems[0].call(el, i, this._render(el.children))
  546. : elems;
  547. const dom = this._makeDomArray(domSrc, i < lastIdx);
  548. // Add element after `this` element
  549. uniqueSplice(siblings, index + 1, 0, dom, el.parent);
  550. });
  551. }
  552. /**
  553. * Insert every element in the set of matched elements after the target.
  554. *
  555. * @category Manipulation
  556. * @example
  557. *
  558. * ```js
  559. * $('<li class="plum">Plum</li>').insertAfter('.apple');
  560. * $.html();
  561. * //=> <ul id="fruits">
  562. * // <li class="apple">Apple</li>
  563. * // <li class="plum">Plum</li>
  564. * // <li class="orange">Orange</li>
  565. * // <li class="pear">Pear</li>
  566. * // </ul>
  567. * ```
  568. *
  569. * @param target - Element to insert elements after.
  570. * @returns The set of newly inserted elements.
  571. * @see {@link https://api.jquery.com/insertAfter/}
  572. */
  573. function insertAfter(target) {
  574. if (typeof target === 'string') {
  575. target = this._make(target);
  576. }
  577. this.remove();
  578. const clones = [];
  579. for (const el of this._makeDomArray(target)) {
  580. const clonedSelf = this.clone().toArray();
  581. const { parent } = el;
  582. if (!parent) {
  583. continue;
  584. }
  585. const siblings = parent.children;
  586. const index = siblings.indexOf(el);
  587. // If not found, move on
  588. /* istanbul ignore next */
  589. if (index === -1)
  590. continue;
  591. // Add cloned `this` element(s) after target element
  592. uniqueSplice(siblings, index + 1, 0, clonedSelf, parent);
  593. clones.push(...clonedSelf);
  594. }
  595. return this._make(clones);
  596. }
  597. /**
  598. * Insert content previous to each element in the set of matched elements.
  599. *
  600. * @category Manipulation
  601. * @example
  602. *
  603. * ```js
  604. * $('.apple').before('<li class="plum">Plum</li>');
  605. * $.html();
  606. * //=> <ul id="fruits">
  607. * // <li class="plum">Plum</li>
  608. * // <li class="apple">Apple</li>
  609. * // <li class="orange">Orange</li>
  610. * // <li class="pear">Pear</li>
  611. * // </ul>
  612. * ```
  613. *
  614. * @param elems - HTML string, DOM element, array of DOM elements or Cheerio to
  615. * insert before each element in the set of matched elements.
  616. * @returns The instance itself.
  617. * @see {@link https://api.jquery.com/before/}
  618. */
  619. function before(...elems) {
  620. const lastIdx = this.length - 1;
  621. return (0, utils_js_1.domEach)(this, (el, i) => {
  622. if (!(0, domhandler_1.hasChildren)(el) || !el.parent) {
  623. return;
  624. }
  625. const siblings = el.parent.children;
  626. const index = siblings.indexOf(el);
  627. // If not found, move on
  628. /* istanbul ignore next */
  629. if (index === -1)
  630. return;
  631. const domSrc = typeof elems[0] === 'function'
  632. ? elems[0].call(el, i, this._render(el.children))
  633. : elems;
  634. const dom = this._makeDomArray(domSrc, i < lastIdx);
  635. // Add element before `el` element
  636. uniqueSplice(siblings, index, 0, dom, el.parent);
  637. });
  638. }
  639. /**
  640. * Insert every element in the set of matched elements before the target.
  641. *
  642. * @category Manipulation
  643. * @example
  644. *
  645. * ```js
  646. * $('<li class="plum">Plum</li>').insertBefore('.apple');
  647. * $.html();
  648. * //=> <ul id="fruits">
  649. * // <li class="plum">Plum</li>
  650. * // <li class="apple">Apple</li>
  651. * // <li class="orange">Orange</li>
  652. * // <li class="pear">Pear</li>
  653. * // </ul>
  654. * ```
  655. *
  656. * @param target - Element to insert elements before.
  657. * @returns The set of newly inserted elements.
  658. * @see {@link https://api.jquery.com/insertBefore/}
  659. */
  660. function insertBefore(target) {
  661. const targetArr = this._make(target);
  662. this.remove();
  663. const clones = [];
  664. (0, utils_js_1.domEach)(targetArr, (el) => {
  665. const clonedSelf = this.clone().toArray();
  666. const { parent } = el;
  667. if (!parent) {
  668. return;
  669. }
  670. const siblings = parent.children;
  671. const index = siblings.indexOf(el);
  672. // If not found, move on
  673. /* istanbul ignore next */
  674. if (index === -1)
  675. return;
  676. // Add cloned `this` element(s) after target element
  677. uniqueSplice(siblings, index, 0, clonedSelf, parent);
  678. clones.push(...clonedSelf);
  679. });
  680. return this._make(clones);
  681. }
  682. /**
  683. * Removes the set of matched elements from the DOM and all their children.
  684. * `selector` filters the set of matched elements to be removed.
  685. *
  686. * @category Manipulation
  687. * @example
  688. *
  689. * ```js
  690. * $('.pear').remove();
  691. * $.html();
  692. * //=> <ul id="fruits">
  693. * // <li class="apple">Apple</li>
  694. * // <li class="orange">Orange</li>
  695. * // </ul>
  696. * ```
  697. *
  698. * @param selector - Optional selector for elements to remove.
  699. * @returns The instance itself.
  700. * @see {@link https://api.jquery.com/remove/}
  701. */
  702. function remove(selector) {
  703. // Filter if we have selector
  704. const elems = selector ? this.filter(selector) : this;
  705. (0, utils_js_1.domEach)(elems, (el) => {
  706. (0, domutils_1.removeElement)(el);
  707. el.prev = el.next = el.parent = null;
  708. });
  709. return this;
  710. }
  711. /**
  712. * Replaces matched elements with `content`.
  713. *
  714. * @category Manipulation
  715. * @example
  716. *
  717. * ```js
  718. * const plum = $('<li class="plum">Plum</li>');
  719. * $('.pear').replaceWith(plum);
  720. * $.html();
  721. * //=> <ul id="fruits">
  722. * // <li class="apple">Apple</li>
  723. * // <li class="orange">Orange</li>
  724. * // <li class="plum">Plum</li>
  725. * // </ul>
  726. * ```
  727. *
  728. * @param content - Replacement for matched elements.
  729. * @returns The instance itself.
  730. * @see {@link https://api.jquery.com/replaceWith/}
  731. */
  732. function replaceWith(content) {
  733. return (0, utils_js_1.domEach)(this, (el, i) => {
  734. const { parent } = el;
  735. if (!parent) {
  736. return;
  737. }
  738. const siblings = parent.children;
  739. const cont = typeof content === 'function' ? content.call(el, i, el) : content;
  740. const dom = this._makeDomArray(cont);
  741. /*
  742. * In the case that `dom` contains nodes that already exist in other
  743. * structures, ensure those nodes are properly removed.
  744. */
  745. (0, parse_js_1.update)(dom, null);
  746. const index = siblings.indexOf(el);
  747. // Completely remove old element
  748. uniqueSplice(siblings, index, 1, dom, parent);
  749. if (!dom.includes(el)) {
  750. el.parent = el.prev = el.next = null;
  751. }
  752. });
  753. }
  754. /**
  755. * Removes all children from each item in the selection. Text nodes and comment
  756. * nodes are left as is.
  757. *
  758. * @category Manipulation
  759. * @example
  760. *
  761. * ```js
  762. * $('ul').empty();
  763. * $.html();
  764. * //=> <ul id="fruits"></ul>
  765. * ```
  766. *
  767. * @returns The instance itself.
  768. * @see {@link https://api.jquery.com/empty/}
  769. */
  770. function empty() {
  771. return (0, utils_js_1.domEach)(this, (el) => {
  772. if (!(0, domhandler_1.hasChildren)(el))
  773. return;
  774. for (const child of el.children) {
  775. child.next = child.prev = child.parent = null;
  776. }
  777. el.children.length = 0;
  778. });
  779. }
  780. function html(str) {
  781. if (str === undefined) {
  782. const el = this[0];
  783. if (!el || !(0, domhandler_1.hasChildren)(el))
  784. return null;
  785. return this._render(el.children);
  786. }
  787. return (0, utils_js_1.domEach)(this, (el) => {
  788. if (!(0, domhandler_1.hasChildren)(el))
  789. return;
  790. for (const child of el.children) {
  791. child.next = child.prev = child.parent = null;
  792. }
  793. const content = (0, utils_js_1.isCheerio)(str)
  794. ? str.toArray()
  795. : this._parse(`${str}`, this.options, false, el).children;
  796. (0, parse_js_1.update)(content, el);
  797. });
  798. }
  799. /**
  800. * Turns the collection to a string. Alias for `.html()`.
  801. *
  802. * @category Manipulation
  803. * @returns The rendered document.
  804. */
  805. function toString() {
  806. return this._render(this);
  807. }
  808. function text(str) {
  809. // If `str` is undefined, act as a "getter"
  810. if (str === undefined) {
  811. return (0, static_js_1.text)(this);
  812. }
  813. if (typeof str === 'function') {
  814. // Function support
  815. return (0, utils_js_1.domEach)(this, (el, i) => this._make(el).text(str.call(el, i, (0, static_js_1.text)([el]))));
  816. }
  817. // Append text node to each selected elements
  818. return (0, utils_js_1.domEach)(this, (el) => {
  819. if (!(0, domhandler_1.hasChildren)(el))
  820. return;
  821. for (const child of el.children) {
  822. child.next = child.prev = child.parent = null;
  823. }
  824. const textNode = new domhandler_1.Text(`${str}`);
  825. (0, parse_js_1.update)(textNode, el);
  826. });
  827. }
  828. /**
  829. * Clone the cheerio object.
  830. *
  831. * @category Manipulation
  832. * @example
  833. *
  834. * ```js
  835. * const moreFruit = $('#fruits').clone();
  836. * ```
  837. *
  838. * @returns The cloned object.
  839. * @see {@link https://api.jquery.com/clone/}
  840. */
  841. function clone() {
  842. const clone = Array.prototype.map.call(this.get(), (el) => (0, domhandler_1.cloneNode)(el, true));
  843. // Add a root node around the cloned nodes
  844. const root = new domhandler_1.Document(clone);
  845. for (const node of clone) {
  846. node.parent = root;
  847. }
  848. return this._make(clone);
  849. }
  850. //# sourceMappingURL=manipulation.js.map