Skip to content

Commit 9806151

Browse files
committed
add iterators for QuerySelector implementations
1 parent 5bee054 commit 9806151

File tree

7 files changed

+58
-4
lines changed

7 files changed

+58
-4
lines changed

document.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dom
33
import (
44
"strings"
55

6+
"github.com/andybalholm/cascadia"
67
"golang.org/x/net/html"
78
"golang.org/x/net/html/atom"
89

@@ -32,6 +33,14 @@ func (d *Document) QuerySelector(query string) spec.Element {
3233
func (d *Document) QuerySelectorAll(query string) spec.NodeList[spec.Element] {
3334
return querySelectorAll(d.node, query, false)
3435
}
36+
37+
func (d *Document) QuerySelectorEach(query string) spec.NodeIterator[spec.Element] {
38+
m := cascadia.MustCompile(query)
39+
return func(yield func(spec.Element) bool) {
40+
querySelectorEach(d.node, m, yield)
41+
}
42+
}
43+
3544
func (d *Document) Contains(other spec.Node) bool { return contains(d.node, other) }
3645

3746
// TextContent returns an empty string.

document_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ func TestDocument_QuerySelectorAll(t *testing.T) {
303303
document := &Document{node: parsedDocument}
304304

305305
result := document.QuerySelectorAll(`#1 [data-find-me]`)
306-
assert.Equal(t, result.Length(), 2)
306+
require.Equal(t, result.Length(), 2)
307307
assert.Equal(t, "x1", result.Item(0).ID())
308308
assert.Equal(t, "x2", result.Item(1).ID())
309309
}

element.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"strings"
66

7+
"github.com/andybalholm/cascadia"
78
"golang.org/x/net/html"
89

910
"github.com/crhntr/dom/spec"
@@ -13,6 +14,13 @@ type Element struct {
1314
node *html.Node
1415
}
1516

17+
func (e *Element) QuerySelectorEach(query string) spec.NodeIterator[spec.Element] {
18+
m := cascadia.MustCompile(query)
19+
return func(yield func(spec.Element) bool) {
20+
querySelectorEach(e.node, m, yield)
21+
}
22+
}
23+
1624
// NewNode
1725

1826
func (e *Element) NodeType() spec.NodeType { return nodeType(e.node.Type) }

element_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import (
1313
"github.com/crhntr/dom/spec"
1414
)
1515

16+
var _ spec.Element = (*Element)(nil)
17+
1618
func TestElement_NodeType(t *testing.T) {
1719
// language=html
1820
parsedDocument, err := html.Parse(strings.NewReader(`<!DOCTYPE html><html lang="us-en"><head><title></title></head><body><span></span></body</html>`))

fragment.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"slices"
66

7+
"github.com/andybalholm/cascadia"
78
"golang.org/x/net/html"
89

910
"github.com/crhntr/dom/spec"
@@ -127,3 +128,17 @@ func (d *DocumentFragment) QuerySelectorAll(query string) spec.NodeList[spec.Ele
127128
}
128129
return slices.Clip(list)
129130
}
131+
132+
func (d *DocumentFragment) QuerySelectorEach(query string) spec.NodeIterator[spec.Element] {
133+
m := cascadia.MustCompile(query)
134+
return func(yield func(spec.Element) bool) {
135+
for _, n := range d.nodes {
136+
if m.Match(n) {
137+
if !yield(&Element{node: n}) {
138+
return
139+
}
140+
}
141+
querySelectorEach(n, m, yield)
142+
}
143+
}
144+
}

node.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -435,11 +435,16 @@ func querySelector(node *html.Node, query string, includeParent bool) spec.Eleme
435435
}
436436

437437
func querySelectorAll(node *html.Node, query string, includeParent bool) nodeListHTMLElements {
438-
q := cascadia.MustCompile(query)
439-
results := cascadia.QueryAll(node, cascadia.MustCompile(query))
440-
if includeParent && node.Type == html.ElementNode && q.Match(node) {
438+
m := cascadia.MustCompile(query)
439+
var results []*html.Node
440+
if includeParent && m.Match(node) {
441441
results = slices.Insert(results, 0, node)
442442
}
443+
querySelectorEach(node, m, func(element spec.Element) bool {
444+
el := element.(*Element)
445+
results = append(results, el.node)
446+
return true
447+
})
443448
return results
444449
}
445450

@@ -488,3 +493,14 @@ func getAttribute(node *html.Node, name string) string {
488493
}
489494
return ""
490495
}
496+
497+
func querySelectorEach(n *html.Node, m cascadia.Matcher, yield func(spec.Element) bool) {
498+
for c := n.FirstChild; c != nil; c = c.NextSibling {
499+
if m.Match(c) {
500+
if !yield(&Element{node: c}) {
501+
return
502+
}
503+
}
504+
querySelectorEach(c, m, yield)
505+
}
506+
}

spec/interfaces.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ type ElementQueries interface {
188188

189189
QuerySelector(query string) Element
190190
QuerySelectorAll(query string) NodeList[Element]
191+
QuerySelectorEach(query string) NodeIterator[Element]
191192
}
192193

193194
// Element is based on
@@ -248,6 +249,7 @@ type DocumentFragment interface {
248249

249250
QuerySelector(query string) Element
250251
QuerySelectorAll(query string) NodeList[Element]
252+
QuerySelectorEach(query string) NodeIterator[Element]
251253
}
252254

253255
type Comment interface {
@@ -256,3 +258,5 @@ type Comment interface {
256258
Data() string
257259
SetData() string
258260
}
261+
262+
type NodeIterator[N Node] func(func(N) bool)

0 commit comments

Comments
 (0)