From ece69fc2fd8795f9826ddc0745a01c3ba12c4080 Mon Sep 17 00:00:00 2001 From: jvoisin Date: Wed, 27 May 2026 21:11:39 +0200 Subject: [PATCH] perf: speed up Selection.Text by outlining recursion Replace the recursive closure in Selection.Text with a helper method call. This keeps behavior unchanged while reducing runtime overhead in the hot path. Benchmark (BenchmarkText, benchstat, n=12): - sec/op: 2.133us -> 1.692us (-20.70%, p=0.000) - B/op: 504 -> 504 (no change) - allocs/op: 6 -> 6 (no change) --- property.go | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/property.go b/property.go index beabc5d..e7f50ee 100644 --- a/property.go +++ b/property.go @@ -59,27 +59,25 @@ func (s *Selection) SetAttr(attrName, val string) *Selection { // elements, including their descendants. func (s *Selection) Text() string { var builder strings.Builder - - // Slightly optimized vs calling Each: no single selection object created - var f func(*html.Node) - f = func(n *html.Node) { - if n.Type == html.TextNode { - // Keep newlines and spaces, like jQuery - builder.WriteString(n.Data) - } - if n.FirstChild != nil { - for c := n.FirstChild; c != nil; c = c.NextSibling { - f(c) - } - } - } for _, n := range s.Nodes { - f(n) + s.textHelper(n, &builder) } return builder.String() } +func (s *Selection) textHelper(n *html.Node, builder *strings.Builder) { + if n.Type == html.TextNode { + // Keep newlines and spaces, like jQuery + builder.WriteString(n.Data) + } + if n.FirstChild != nil { + for c := n.FirstChild; c != nil; c = c.NextSibling { + s.textHelper(c, builder) + } + } +} + // Size is an alias for Length. func (s *Selection) Size() int { return s.Length()