I am trying to solve an exercise from The go programming language book: The starting code can be found here: exercise.
What I need to do:
Modify forEachNode so that the pre and post functions return a boolean result indicating whether to continue the traversal. Use it to write a function ElementByID with the following signature that finds the first HTML element with the specified id attribute. The function should stop the traversal as soon as a match is found.
Signature: func ElementByID(doc *html.Node, id string) *html.Node
What I did:
func ElementByID(doc *html.Node, id string) *html.Node {
if doc.Data == id {
fmt.Printf(" %s: %s
", "found", doc.Data)
return doc
}
return nil
}
func startElement(n *html.Node) bool {
if n.Type == html.ElementNode {
if ElementById(n, "a") != nil {
return true
}
fmt.Printf("%*s<%s>
", depth*2, "", n.Data)
depth++
}
return false
}
func endElement(n *html.Node) bool {
if n.Type == html.ElementNode {
if ElementById(n, "a") != nil {
return true
}
depth--
fmt.Printf("%*s</%s>
", depth*2, "", n.Data)
}
return false
}
Is the above right?, or I've missed something? How can I stop the traversal where element is found?
The forEachNode is the same, only the pre and post signature was changed to return a bool.
You can create a closure and "close" found
node. Example below.
Modify forEachNode so that the pre and post functions return a boolean result indicating whether to continue the traversal.:
func forEachNode(n *html.Node, pre, post func(n *html.Node) bool) {
if pre != nil && !pre(n) {
return
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
forEachNode(c, pre, post)
}
if post != nil && !post(n) {
return
}
}
Use it to write a function ElementByID with the following signature that finds the first HTML element with the specified id attribute. The function should stop the traversal as soon as a match is found.:
func ElementByID(doc *html.Node, id string) *html.Node {
var found *html.Node
pre := func(n *html.Node) bool {
for _, a := range n.Attr {
if a.Key == "id" && a.Val == id {
found = n // memorize matching node
return false // stop traversing
}
}
return true
}
forEachNode(doc, pre, nil)
return found
}