mirror of
https://github.com/PuerkitoBio/goquery
synced 2025-10-05 00:42:50 +08:00
add NextAll(), NextAllFiltered(), refactor all filtered methods
This commit is contained in:
4
doc.go
4
doc.go
@@ -133,14 +133,14 @@ package goquery
|
|||||||
// x Last()
|
// x Last()
|
||||||
// x Length() / Size()
|
// x Length() / Size()
|
||||||
// x Map()
|
// x Map()
|
||||||
// - Next() - Tree traversal
|
// x Next() - Tree traversal
|
||||||
// - NextAll() - Tree traversal
|
// - NextAll() - Tree traversal
|
||||||
// - NextUntil() - Tree traversal
|
// - NextUntil() - Tree traversal
|
||||||
// x Not()
|
// x Not()
|
||||||
// x Parent() - Tree traversal
|
// x Parent() - Tree traversal
|
||||||
// x Parents() - Tree traversal
|
// x Parents() - Tree traversal
|
||||||
// x ParentsUntil() - Tree traversal
|
// x ParentsUntil() - Tree traversal
|
||||||
// - Prev() - Tree traversal
|
// x Prev() - Tree traversal
|
||||||
// - PrevAll() - Tree traversal
|
// - PrevAll() - Tree traversal
|
||||||
// - PrevUntil() - Tree traversal
|
// - PrevUntil() - Tree traversal
|
||||||
// x PushStack()
|
// x PushStack()
|
||||||
|
143
traversal.go
143
traversal.go
@@ -5,6 +5,16 @@ import (
|
|||||||
"exp/html"
|
"exp/html"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type siblingType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
siblintPrevAll siblingType = iota - 2
|
||||||
|
siblingPrev
|
||||||
|
siblingAll
|
||||||
|
siblingNext
|
||||||
|
siblingNextAll
|
||||||
|
)
|
||||||
|
|
||||||
// Find() gets the descendants of each element in the current set of matched
|
// Find() gets the descendants of each element in the current set of matched
|
||||||
// elements, filtered by a selector. It returns a new Selection object
|
// elements, filtered by a selector. It returns a new Selection object
|
||||||
// containing these matched elements.
|
// containing these matched elements.
|
||||||
@@ -19,12 +29,7 @@ func (this *Selection) FindSelection(sel *Selection) *Selection {
|
|||||||
if sel == nil {
|
if sel == nil {
|
||||||
return pushStack(this, nil)
|
return pushStack(this, nil)
|
||||||
}
|
}
|
||||||
|
return this.FindNodes(sel.Nodes...)
|
||||||
// Filter the specified Selection to only the current nodes that
|
|
||||||
// contain one of the Selection.
|
|
||||||
return sel.FilterFunction(func(i int, s *Selection) bool {
|
|
||||||
return sliceContains(this.Nodes, s.Nodes[0])
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindNodes() gets the descendants of each element in the current
|
// FindNodes() gets the descendants of each element in the current
|
||||||
@@ -71,14 +76,7 @@ func (this *Selection) Children() *Selection {
|
|||||||
// filtered by the specified selector. It returns a new
|
// filtered by the specified selector. It returns a new
|
||||||
// Selection object containing these elements.
|
// Selection object containing these elements.
|
||||||
func (this *Selection) ChildrenFiltered(selector string) *Selection {
|
func (this *Selection) ChildrenFiltered(selector string) *Selection {
|
||||||
// Get the Children() unfiltered
|
return filterAndPush(this, getSelectionChildren(this, true), selector)
|
||||||
nodes := getSelectionChildren(this, true)
|
|
||||||
// Create a temporary Selection to filter using winnow
|
|
||||||
sel := &Selection{nodes, this.document, nil}
|
|
||||||
// Filter based on selector
|
|
||||||
nodes = winnow(sel, selector, true)
|
|
||||||
// Push on the stack and return the "real" Selection
|
|
||||||
return pushStack(this, nodes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parent() gets the parent of each element in the Selection. It returns a
|
// Parent() gets the parent of each element in the Selection. It returns a
|
||||||
@@ -90,13 +88,7 @@ func (this *Selection) Parent() *Selection {
|
|||||||
// ParentFiltered() gets the parent of each element in the Selection filtered by a
|
// ParentFiltered() gets the parent of each element in the Selection filtered by a
|
||||||
// selector. It returns a new Selection object containing the matched elements.
|
// selector. It returns a new Selection object containing the matched elements.
|
||||||
func (this *Selection) ParentFiltered(selector string) *Selection {
|
func (this *Selection) ParentFiltered(selector string) *Selection {
|
||||||
// Get the Parent() unfiltered
|
return filterAndPush(this, getParentNodes(this.Nodes), selector)
|
||||||
nodes := getParentNodes(this.Nodes)
|
|
||||||
// Create a temporary Selection to filter using winnow
|
|
||||||
sel := &Selection{nodes, this.document, nil}
|
|
||||||
// Filter based on selector
|
|
||||||
nodes = winnow(sel, selector, true)
|
|
||||||
return pushStack(this, nodes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parents() gets the ancestors of each element in the current Selection. It
|
// Parents() gets the ancestors of each element in the current Selection. It
|
||||||
@@ -108,13 +100,7 @@ func (this *Selection) Parents() *Selection {
|
|||||||
// ParentsFiltered() gets the ancestors of each element in the current
|
// ParentsFiltered() gets the ancestors of each element in the current
|
||||||
// Selection. It returns a new Selection object with the matched elements.
|
// Selection. It returns a new Selection object with the matched elements.
|
||||||
func (this *Selection) ParentsFiltered(selector string) *Selection {
|
func (this *Selection) ParentsFiltered(selector string) *Selection {
|
||||||
// Get the Parents() unfiltered
|
return filterAndPush(this, getParentsNodes(this.Nodes, "", nil), selector)
|
||||||
nodes := getParentsNodes(this.Nodes, "", nil)
|
|
||||||
// Create a temporary Selection to filter using winnow
|
|
||||||
sel := &Selection{nodes, this.document, nil}
|
|
||||||
// Filter based on selector
|
|
||||||
nodes = winnow(sel, selector, true)
|
|
||||||
return pushStack(this, nodes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParentsUntil() gets the ancestors of each element in the Selection, up to but
|
// ParentsUntil() gets the ancestors of each element in the Selection, up to but
|
||||||
@@ -145,14 +131,7 @@ func (this *Selection) ParentsUntilNodes(nodes ...*html.Node) *Selection {
|
|||||||
// results based on a selector string. It returns a new Selection
|
// results based on a selector string. It returns a new Selection
|
||||||
// object containing the matched elements.
|
// object containing the matched elements.
|
||||||
func (this *Selection) ParentsFilteredUntil(filterSelector string, untilSelector string) *Selection {
|
func (this *Selection) ParentsFilteredUntil(filterSelector string, untilSelector string) *Selection {
|
||||||
|
return filterAndPush(this, getParentsNodes(this.Nodes, untilSelector, nil), filterSelector)
|
||||||
// Get the ParentsUntil() unfiltered
|
|
||||||
nodes := getParentsNodes(this.Nodes, untilSelector, nil)
|
|
||||||
// Create a temporary Selection to filter using winnow
|
|
||||||
sel := &Selection{nodes, this.document, nil}
|
|
||||||
// Filter based on selector
|
|
||||||
nodes = winnow(sel, filterSelector, true)
|
|
||||||
return pushStack(this, nodes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParentsFilteredUntilSelection() is like ParentsUntilSelection(), with the
|
// ParentsFilteredUntilSelection() is like ParentsUntilSelection(), with the
|
||||||
@@ -169,71 +148,68 @@ func (this *Selection) ParentsFilteredUntilSelection(filterSelector string, sel
|
|||||||
// option to filter the results based on a selector string. It returns a new
|
// option to filter the results based on a selector string. It returns a new
|
||||||
// Selection object containing the matched elements.
|
// Selection object containing the matched elements.
|
||||||
func (this *Selection) ParentsFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
|
func (this *Selection) ParentsFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
|
||||||
|
return filterAndPush(this, getParentsNodes(this.Nodes, "", nodes), filterSelector)
|
||||||
// Get the ParentsUntilNodes() unfiltered
|
|
||||||
n := getParentsNodes(this.Nodes, "", nodes)
|
|
||||||
// Create a temporary Selection to filter using winnow
|
|
||||||
sel := &Selection{n, this.document, nil}
|
|
||||||
// Filter based on selector
|
|
||||||
n = winnow(sel, filterSelector, true)
|
|
||||||
return pushStack(this, n)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Siblings() gets the siblings of each element in the Selection. It returns
|
// Siblings() gets the siblings of each element in the Selection. It returns
|
||||||
// a new Selection object containing the matched elements.
|
// a new Selection object containing the matched elements.
|
||||||
func (this *Selection) Siblings() *Selection {
|
func (this *Selection) Siblings() *Selection {
|
||||||
return pushStack(this, getSiblingNodes(this.Nodes, 0))
|
return pushStack(this, getSiblingNodes(this.Nodes, siblingAll))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SiblingsFiltered() gets the siblings of each element in the Selection
|
// SiblingsFiltered() gets the siblings of each element in the Selection
|
||||||
// filtered by a selector. It returns a new Selection object containing the
|
// filtered by a selector. It returns a new Selection object containing the
|
||||||
// matched elements.
|
// matched elements.
|
||||||
func (this *Selection) SiblingsFiltered(selector string) *Selection {
|
func (this *Selection) SiblingsFiltered(selector string) *Selection {
|
||||||
// Get the Siblings() unfiltered
|
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingAll), selector)
|
||||||
n := getSiblingNodes(this.Nodes, 0)
|
|
||||||
// Create a temporary Selection to filter using winnow
|
|
||||||
sel := &Selection{n, this.document, nil}
|
|
||||||
// Filter based on selector
|
|
||||||
n = winnow(sel, selector, true)
|
|
||||||
return pushStack(this, n)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next() gets the immediately following sibling of each element in the
|
// Next() gets the immediately following sibling of each element in the
|
||||||
// Selection. It returns a new Selection object containing the matched elements.
|
// Selection. It returns a new Selection object containing the matched elements.
|
||||||
func (this *Selection) Next() *Selection {
|
func (this *Selection) Next() *Selection {
|
||||||
return pushStack(this, getSiblingNodes(this.Nodes, 1))
|
return pushStack(this, getSiblingNodes(this.Nodes, siblingNext))
|
||||||
}
|
}
|
||||||
|
|
||||||
// NextFiltered() gets the immediately following sibling of each element in the
|
// NextFiltered() gets the immediately following sibling of each element in the
|
||||||
// Selection filtered by a selector. It returns a new Selection object
|
// Selection filtered by a selector. It returns a new Selection object
|
||||||
// containing the matched elements.
|
// containing the matched elements.
|
||||||
func (this *Selection) NextFiltered(selector string) *Selection {
|
func (this *Selection) NextFiltered(selector string) *Selection {
|
||||||
// Get the Next() unfiltered
|
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingNext), selector)
|
||||||
n := getSiblingNodes(this.Nodes, 1)
|
}
|
||||||
// Create a temporary Selection to filter using winnow
|
|
||||||
sel := &Selection{n, this.document, nil}
|
// NextAll() gets all the following siblings of each element in the
|
||||||
// Filter based on selector
|
// Selection. It returns a new Selection object containing the matched elements.
|
||||||
n = winnow(sel, selector, true)
|
func (this *Selection) NextAll() *Selection {
|
||||||
return pushStack(this, n)
|
return pushStack(this, getSiblingNodes(this.Nodes, siblingNextAll))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextAllFiltered() gets all the following siblings of each element in the
|
||||||
|
// Selection filtered by a selector. It returns a new Selection object
|
||||||
|
// containing the matched elements.
|
||||||
|
func (this *Selection) NextAllFiltered(selector string) *Selection {
|
||||||
|
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingNextAll), selector)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prev() gets the immediately preceding sibling of each element in the
|
// Prev() gets the immediately preceding sibling of each element in the
|
||||||
// Selection. It returns a new Selection object containing the matched elements.
|
// Selection. It returns a new Selection object containing the matched elements.
|
||||||
func (this *Selection) Prev() *Selection {
|
func (this *Selection) Prev() *Selection {
|
||||||
return pushStack(this, getSiblingNodes(this.Nodes, -1))
|
return pushStack(this, getSiblingNodes(this.Nodes, siblingPrev))
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrevFiltered() gets the immediately preceding sibling of each element in the
|
// PrevFiltered() gets the immediately preceding sibling of each element in the
|
||||||
// Selection filtered by a selector. It returns a new Selection object
|
// Selection filtered by a selector. It returns a new Selection object
|
||||||
// containing the matched elements.
|
// containing the matched elements.
|
||||||
func (this *Selection) PrevFiltered(selector string) *Selection {
|
func (this *Selection) PrevFiltered(selector string) *Selection {
|
||||||
// Get the Prev() unfiltered
|
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingPrev), selector)
|
||||||
n := getSiblingNodes(this.Nodes, -1)
|
}
|
||||||
// Create a temporary Selection to filter using winnow
|
|
||||||
sel := &Selection{n, this.document, nil}
|
// Filter and push filters the nodes based on a selector, and pushes the results
|
||||||
// Filter based on selector
|
// on the stack, with the srcSel as previous selection.
|
||||||
n = winnow(sel, selector, true)
|
func filterAndPush(srcSel *Selection, nodes []*html.Node, selector string) *Selection {
|
||||||
return pushStack(this, n)
|
// Create a temporary Selection with the specified nodes to filter using winnow
|
||||||
|
sel := &Selection{nodes, srcSel.document, nil}
|
||||||
|
// Filter based on selector and push on stack
|
||||||
|
return pushStack(srcSel, winnow(sel, selector, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal implementation to get all parent nodes, stopping at the specified
|
// Internal implementation to get all parent nodes, stopping at the specified
|
||||||
@@ -260,13 +236,10 @@ func getParentsNodes(nodes []*html.Node, stopSelector string, stopNodes []*html.
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Internal implementation of sibling nodes that return a raw slice of matches.
|
// Internal implementation of sibling nodes that return a raw slice of matches.
|
||||||
func getSiblingNodes(nodes []*html.Node, siblingType int) []*html.Node {
|
func getSiblingNodes(nodes []*html.Node, st siblingType) []*html.Node {
|
||||||
// Sibling type means:
|
|
||||||
// -1 : previous node only
|
|
||||||
// 1 : next node only
|
|
||||||
// 0 : all but itself
|
|
||||||
return mapNodes(nodes, func(i int, n *html.Node) (result []*html.Node) {
|
return mapNodes(nodes, func(i int, n *html.Node) (result []*html.Node) {
|
||||||
var prev *html.Node
|
var prev *html.Node
|
||||||
|
var nFound bool
|
||||||
|
|
||||||
// Get the parent and loop through all children
|
// Get the parent and loop through all children
|
||||||
if p := n.Parent; p != nil {
|
if p := n.Parent; p != nil {
|
||||||
@@ -274,20 +247,28 @@ func getSiblingNodes(nodes []*html.Node, siblingType int) []*html.Node {
|
|||||||
// Care only about elements
|
// Care only about elements
|
||||||
if c.Type == html.ElementNode {
|
if c.Type == html.ElementNode {
|
||||||
// Is it the existing node?
|
// Is it the existing node?
|
||||||
if c == n && siblingType == -1 {
|
if c == n {
|
||||||
// We want the previous node only, so append it and return
|
// Found the current node
|
||||||
if prev != nil {
|
nFound = true
|
||||||
result = append(result, prev)
|
if st == siblingPrev {
|
||||||
|
// We want the previous node only, so append it and return
|
||||||
|
if prev != nil {
|
||||||
|
result = append(result, prev)
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return
|
} else if prev == n && st == siblingNext {
|
||||||
} else if prev == n && siblingType == 1 {
|
|
||||||
// We want only the next node and this is it, so append it and return
|
// We want only the next node and this is it, so append it and return
|
||||||
result = append(result, c)
|
result = append(result, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Keep child as previous
|
||||||
prev = c
|
prev = c
|
||||||
if c != n && siblingType == 0 {
|
|
||||||
// This is not the original node, so append it
|
// If child is not the current node, check if sibling type requires
|
||||||
|
// to add it to the result.
|
||||||
|
if c != n && (st == siblingAll || (st == siblintPrevAll && !nFound) ||
|
||||||
|
(st == siblingNextAll && nFound)) {
|
||||||
result = append(result, c)
|
result = append(result, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user