diff --git a/lib/api/manipulation.js b/lib/api/manipulation.js index 27d553e25e..24a6318174 100644 --- a/lib/api/manipulation.js +++ b/lib/api/manipulation.js @@ -364,6 +364,47 @@ exports.wrapInner = _wrap(function (el, elInsertLocation, wrapperDom) { updateDOM(wrapperDom, el); }); +/** + * The .unwrap() function, removes the parents of the set of matched elements + * from the DOM, leaving the matched elements in their place. + * + * @example without selector + * const $ = cheerio.load( + * '
\n

Hello

\n

World

\n
' + * ); + * $('#test p').unwrap(); + * + * //=>
+ * //

Hello

+ * //

World

+ * //
+ * + * @example with selector + * const $ = cheerio.load( + * '
\n

Hello

\n

World

\n
' + * ); + * $('#test p').unwrap('b'); + * + * //=>
+ * //

Hello

+ * //

World

+ * //
+ * + * @param {string} [selector] - A selector to check the parent element against. If + * an element's parent does not match the selector, the element won't be unwrapped. + * @see {@link https://api.jquery.com/unwrap/} + * @returns {Cheerio} The instance itself, for chaining. + */ +exports.unwrap = function (selector) { + var self = this; + this.parent(selector) + .not('body') + .each(function (i, el) { + self._make(el).replaceWith(el.children); + }); + return this; +}; + /** * The .wrapAll() function can take any string or object that could be passed to * the $() function to specify a DOM structure. This structure may be nested diff --git a/test/api/manipulation.js b/test/api/manipulation.js index b1476944db..ced60650aa 100644 --- a/test/api/manipulation.js +++ b/test/api/manipulation.js @@ -324,6 +324,89 @@ describe('$(...)', function () { }); }); + describe('.unwrap', function () { + var $elem; + var unwrapspans = [ + '', + ].join(''); + + beforeEach(function () { + $elem = cheerio.load(unwrapspans); + }); + + it('() : should be unwrap span elements', function () { + var abcd = $elem('#unwrap1 > span, #unwrap2 > span').get(); + var abcdef = $elem('#unwrap span').get(); + + // make #unwrap1 and #unwrap2 go away + expect( + $elem('#unwrap1 span').add('#unwrap2 span:first-child').unwrap() + ).toHaveLength(3); + + //.toEqual + // all four spans should still exist + expect($elem('#unwrap > span').get()).toEqual(abcd); + + // make all b elements in #unwrap3 go away + expect($elem('#unwrap3 span').unwrap().get()).toEqual( + $elem('#unwrap3 > span').get() + ); + + // make #unwrap3 go away + expect($elem('#unwrap3 span').unwrap().get()).toEqual( + $elem('#unwrap > span.unwrap3').get() + ); + + // #unwrap only contains 6 child spans + expect($elem('#unwrap').children().get()).toEqual(abcdef); + + // make the 6 spans become children of body + expect($elem('#unwrap > span').unwrap().get()).toEqual( + $elem('body > span.unwrap').get() + ); + + // can't unwrap children of body + expect($elem('body > span.unwrap').unwrap().get()).toEqual( + $elem('body > span.unwrap').get() + ); + + // can't unwrap children of body + expect($elem('body > span.unwrap').unwrap().get()).toEqual(abcdef); + + // can't unwrap children of body + expect($elem('body > span.unwrap').get()).toEqual(abcdef); + }); + + it('(selector) : should only unwrap element parent what specified', function () { + var abcd = $elem('#unwrap1 > span, #unwrap2 > span').get(); + // var abcdef = $elem('#unwrap span').get(); + + // Shouldn't unwrap, no match + $elem('#unwrap1 span').unwrap('#unwrap2'); + expect($elem('#unwrap1')).toHaveLength(1); + + // Shouldn't unwrap, no match + $elem('#unwrap1 span').unwrap('span'); + expect($elem('#unwrap1')).toHaveLength(1); + + // Unwraps + $elem('#unwrap1 span').unwrap('#unwrap1'); + expect($elem('#unwrap1')).toHaveLength(0); + + // Should not unwrap - unmatched unwrap + $elem('#unwrap2 span').unwrap('quote'); + expect($elem('#unwrap > span')).toHaveLength(2); + + // Check return values - matched unwrap + $elem('#unwrap2 span').unwrap('#unwrap2'); + expect($elem('#unwrap > span').get()).toEqual(abcd); + }); + }); + describe('.wrapAll', function () { var doc; var $inner;