diff --git a/doc.go b/doc.go index 5f29bb1..9425fcd 100644 --- a/doc.go +++ b/doc.go @@ -95,6 +95,9 @@ The various methods are split into files based on the category of behavior: */ package goquery +// TODO : Test End() on all filtering/expanding/array/traversal functions, make +// sure it returns the same object as the previous selection. + // DONE array.go : Positional Manipulation: First(), Last(), Eq(), Get(), Index(), Slice() // DONE filter.go : Filtering: Filter(), Not(), Has(), End() // DONE expand.go : "Expanding": Add(), AndSelf() @@ -115,7 +118,7 @@ package goquery // x Children() // - Closest() - Tree Traversal // x Contains() -// - Contents() (similar to Children(), but includes text and comment nodes, so Children() should filter them out) - Misc. Traversing +// x Contents() // x Each() // x End() // x Eq() @@ -125,7 +128,7 @@ package goquery // x Get() // x Has() // x HasClass() -// - Html() ? - Attributes +// x Html() // x Index() // x Is() - Filtering // x Last() @@ -147,4 +150,3 @@ package goquery // x Text() - DOM Manipulation // x ToArray() // x Unique() internally only -// - Val() ? - Attributes diff --git a/iteration_test.go b/iteration_test.go index b5993cd..66fbb75 100644 --- a/iteration_test.go +++ b/iteration_test.go @@ -21,6 +21,22 @@ func TestEach(t *testing.T) { } } +func TestEachEmptySelection(t *testing.T) { + var cnt int + + sel := Doc().Find("zzzz") + sel.Each(func(i int, n *Selection) { + cnt++ + }) + if cnt > 0 { + t.Error("Expected Each() to not be called on empty Selection.") + } + sel2 := sel.Find("div") + if sel2.Nodes != nil { + t.Error("Expected Find() on empty Selection to return an empty Selection.") + } +} + func TestMap(t *testing.T) { sel := Doc().Find(".pvk-content") vals := sel.Map(func(i int, s *Selection) string { diff --git a/traversal.go b/traversal.go index bcfe2de..2717ace 100644 --- a/traversal.go +++ b/traversal.go @@ -3,7 +3,6 @@ package goquery import ( "code.google.com/p/cascadia" "exp/html" - "strings" ) // TODO : Maybe make the Document's Root return a Selection, Find() on the @@ -40,24 +39,52 @@ func findWithContext(selector string, nodes ...*html.Node) []*html.Node { return matches } -// TODO : Tests and doc for contents and children - +// Contents() gets the children of each element in the Selection, +// including text and comment nodes. It returns a new Selection object +// containing these elements. func (this *Selection) Contents() *Selection { - var matches []*html.Node - - for _, n := range this.Nodes { - matches = appendWithoutDuplicates(matches, getChildren(n, false)) - } - return pushStack(this, matches) + return pushStack(this, getSelectionChildren(this, false)) } -func (this *Selection) Children() *Selection { - var matches []*html.Node - - for _, n := range this.Nodes { - matches = appendWithoutDuplicates(matches, getChildren(n, true)) +// ContentsFiltered() gets the children of each element in the Selection, +// filtered by the specified selector. It returns a new Selection +// object containing these elements. Since selectors only act on Element nodes, +// this function is an alias to ChildrenFiltered() unless the selector is empty, +// in which case it is an alias to Contents(). +func (this *Selection) ContentsFiltered(selector string) *Selection { + if selector != "" { + return this.ChildrenFiltered(selector) } - return pushStack(this, matches) + return this.Contents() +} + +// Children() gets the child elements of each element in the Selection. +// It returns a new Selection object containing these elements. +func (this *Selection) Children() *Selection { + return pushStack(this, getSelectionChildren(this, true)) +} + +// ChildrenFiltered() gets the child elements of each element in the Selection, +// filtered by the specified selector. It returns a new +// Selection object containing these elements. +func (this *Selection) ChildrenFiltered(selector string) *Selection { + // Get the Children() unfiltered + 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) +} + +// Return the child nodes of each node in the Selection object, without +// duplicates. +func getSelectionChildren(s *Selection, elemOnly bool) (result []*html.Node) { + for _, n := range s.Nodes { + result = appendWithoutDuplicates(result, getChildren(n, elemOnly)) + } + return } // Return the immediate children of the node, filtered on element nodes only diff --git a/traversal_test.go b/traversal_test.go index a04687e..5e48b51 100644 --- a/traversal_test.go +++ b/traversal_test.go @@ -28,22 +28,6 @@ func TestFindInvalidSelector(t *testing.T) { Doc().Find(":+ ^") } -func TestEachEmptySelection(t *testing.T) { - var cnt int - - sel := Doc().Find("zzzz") - sel.Each(func(i int, n *Selection) { - cnt++ - }) - if cnt > 0 { - t.Error("Expected Each() to not be called on empty Selection.") - } - sel2 := sel.Find("div") - if sel2.Nodes != nil { - t.Error("Expected Find() on empty Selection to return an empty Selection.") - } -} - func TestChainedFind(t *testing.T) { sel := Doc().Find("div.hero-unit").Find(".row-fluid") if sel.Nodes == nil || len(sel.Nodes) != 4 { @@ -53,6 +37,16 @@ func TestChainedFind(t *testing.T) { func TestChildren(t *testing.T) { sel := Doc().Find(".pvk-content").Children() + if len(sel.Nodes) != 5 { + t.Errorf("Expected 5 child nodes, got %v.", len(sel.Nodes)) + for _, n := range sel.Nodes { + t.Logf("%+v", n) + } + } +} + +func TestContents(t *testing.T) { + sel := Doc().Find(".pvk-content").Contents() if len(sel.Nodes) != 13 { t.Errorf("Expected 13 child nodes, got %v.", len(sel.Nodes)) for _, n := range sel.Nodes { @@ -71,6 +65,16 @@ func TestChildrenFiltered(t *testing.T) { } } +func TestContentsFiltered(t *testing.T) { + sel := Doc().Find(".pvk-content").ContentsFiltered(".hero-unit") + if len(sel.Nodes) != 1 { + t.Errorf("Expected 1 child nodes, got %v.", len(sel.Nodes)) + for _, n := range sel.Nodes { + t.Logf("%+v", n) + } + } +} + func TestChildrenFilteredNone(t *testing.T) { sel := Doc().Find(".pvk-content").ChildrenFiltered("a.btn") if len(sel.Nodes) != 0 {