From f4eff0926c8785fa265d8f44b10378b20d474236 Mon Sep 17 00:00:00 2001 From: abo3losh1 Date: Wed, 14 Oct 2026 23:59:52 +0200 Subject: [PATCH 1/2] Parse the WrapAllHtml wrapper in the parent's context WrapAllHtml parsed the wrapper with the first selected node itself as the context, not its parent. With a textarea, iframe, script, style, title or xmp the wrapper became a text node and the selection was moved into it, so the element disappeared from the document. With a text or comment node it panicked. Use the parent, as WrapHtml does. --- manipulation.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/manipulation.go b/manipulation.go index ce7731d..43b676b 100644 --- a/manipulation.go +++ b/manipulation.go @@ -449,8 +449,10 @@ func (s *Selection) WrapAllHtml(htmlStr string) *Selection { var context *html.Node var nodes []*html.Node if len(s.Nodes) > 0 { - context = s.Nodes[0] - if context.Parent != nil { + // The wrapper takes the place of the first node, so parse it in the + // context of that node's parent, as WrapHtml does. + context = s.Nodes[0].Parent + if context != nil && context.Type == html.ElementNode { nodes = parseHtmlWithContext(htmlStr, context, s.parseOpts()) } else { nodes = parseHtml(htmlStr, s.parseOpts()) From 9f4d9521c371bc1d2ad774cae481326bff3752b2 Mon Sep 17 00:00:00 2001 From: abo3losh1 Date: Wed, 14 Oct 2026 23:59:52 +0200 Subject: [PATCH 2/2] Test WrapAllHtml on a raw text element and on text nodes --- manipulation_test.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/manipulation_test.go b/manipulation_test.go index cb5145b..8415ecf 100644 --- a/manipulation_test.go +++ b/manipulation_test.go @@ -618,6 +618,22 @@ func TestWrapAllHtml(t *testing.T) { printSel(t, doc.Selection) } +func TestWrapAllHtmlRawTextElement(t *testing.T) { + // The wrapper must be parsed in the parent's context. Parsed inside a + // textarea it would become text, and the textarea would be lost. + doc := loadString(t, ``) + doc.Find("textarea").WrapAllHtml(`
`) + assertLength(t, doc.Find("body > div.wrap > textarea").Nodes, 1) + printSel(t, doc.Selection) +} + +func TestWrapAllHtmlTextNode(t *testing.T) { + doc := loadString(t, `

a b

`) + doc.Find("p").Contents().WrapAllHtml(``) + assertLength(t, doc.Find("p > span.wrap > b").Nodes, 1) + printSel(t, doc.Selection) +} + func TestWrapInnerNoContent(t *testing.T) { doc := Doc2Clone() doc.Find(".one").WrapInner(".two")