manipulation.js 25 KB

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