golang.org/x/net/html.ParseFragmentWithOptions initializes its form
pointer based on whether the context element or any of its ancestors
is a form element. If the context has a form ancestor, nested form tags
are discarded per HTML parsing rules.
Previously, cachedParseHtmlWithContext keyed cached fragments strictly
by context tag name. When a selection contained targets both inside and
outside forms sharing the same tag name, the first visited target cached
its parsed fragment, leading to incorrect parsing for subsequent targets
under the other form-ancestor state.
Differentiate the fragment cache key by appending a form-ancestor
indicator when context or any of its ancestors is a form element.
Fixes#614
goquery calls html.Parse, so callers cannot reach the parse options that
golang.org/x/net/html has since grown. The one that matters is
ParseOptionEnableScripting(false): with scripting assumed enabled the
contents of a noscript element are parsed as raw text and no selector
can reach inside it.
Add a constructor taking html.ParseOption values. Existing constructors
are untouched.
Refs #139
RemoveClass deleted each class with one strings.ReplaceAll pass over the
space-wrapped class list. Two adjacent occurrences share the space
between them, so " a a " holds only one non-overlapping " a " and the
second survived: RemoveClass("a") on class="a a" returned with
HasClass("a") still true. ToggleClass used the same expression and so
toggled such a class the wrong way.
Replace one occurrence at a time, re-scanning from the start, in a helper
shared by both call sites. Whitespace between the remaining classes is
untouched.
Adds goquery.Text(s, *TextOptions), a package-level counterpart to the
Selection.Text method that gives control over how the text of distinct
text nodes is joined (Separator), whether each node is trimmed of
surrounding whitespace (Trim), and which text nodes are included (Keep).
This covers the common need to extract clean, readable text from a
document - for example joining fragments with a space and dropping the
text of script/style elements - without hand-rolling a node walk.
Passing a nil TextOptions keeps the behaviour identical to Selection.Text.
Includes tests and a runnable example.
Attr was a 4-line wrapper around getAttributeValue, which itself was
a 6-line wrapper around getAttributePtr.
This commit inlines getAttributeValue into Attr and
have AttrOr delegate to Attr. Drops a layer of indirection and one
helper function; both methods get shorter and the empty-selection
check lives in one place.
WrapHtml, WrapInnerHtml, and eachNodeHtml each open-coded the same
lookup-or-parse-and-store sequence against a map[string][]*html.Node
keyed by the context node's name. Extract that 4-line sequence into
a tiny cachedParseHtmlWithContext(cache, htmlStr, context) helper; each call
site becomes a linear, closure-free loop and the only duplicated bit (the cache
lookup) lives in one place.
winnow only ever reads sel.Nodes; the *Selection parameter was
incidental. Change its signature to winnow(nodes []*html.Node, ...)
and update its three callers (FilterMatcher, NotMatcher,
filterAndPush). filterAndPush no longer needs to fabricate a
throwaway Selection to satisfy the call.
While this change has o measurable runtime effect, it simplifies the code a bit,
by making winnow's signature clearer, and removes a now-useless line and its
associated comment in filterAndPush
parseHtml and parseHtmlWithContext duplicated the ParseFragment call,
the error check, and the panic message. parseHtml only differed by
passing a synthetic ElementNode as the context.
This commit simply delegates parseHtml to parseHtmlWithContext.