DOM Queries
This page covers how to find and inspect DOM elements using CSS selectors.
See also: Home | Getting-Started | Actions | Console-Monitoring | Angular-Testing
QuerySelector
QuerySelector finds the first element matching a CSS selector and returns an *ElementInfo struct containing the node ID, tag name, attributes, and bounding box.
elem, err := wv.QuerySelector("#main-heading")
if err != nil {
log.Printf("element not found: %v", err)
return
}
log.Printf("Tag: %s", elem.TagName)
log.Printf("ID: %s", elem.Attributes["id"])
log.Printf("Class: %s", elem.Attributes["class"])
if elem.BoundingBox != nil {
log.Printf("Position: (%.0f, %.0f) Size: %.0f x %.0f",
elem.BoundingBox.X, elem.BoundingBox.Y,
elem.BoundingBox.Width, elem.BoundingBox.Height)
}
Under the hood, QuerySelector uses the CDP DOM.getDocument method to obtain the root node, then DOM.querySelector to find the matching element. It returns an error if no element matches.
QuerySelectorAll
QuerySelectorAll returns a slice of all elements matching the selector:
links, err := wv.QuerySelectorAll("a[href]")
if err != nil {
log.Fatal(err)
}
for _, link := range links {
href := link.Attributes["href"]
text := link.Attributes["title"]
log.Printf("Link: %s -> %s", text, href)
}
This uses DOM.querySelectorAll to retrieve all matching node IDs, then calls DOM.describeNode and DOM.getBoxModel for each to populate the ElementInfo structs.
ElementInfo
The ElementInfo struct provides everything you need to know about a DOM element:
type ElementInfo struct {
NodeID int // CDP node identifier
TagName string // Element tag name (e.g., "DIV", "INPUT")
Attributes map[string]string // All HTML attributes (id, class, href, etc.)
InnerHTML string // Inner HTML content (when populated)
InnerText string // Text content (when populated)
BoundingBox *BoundingBox // Visual position and size (may be nil)
}
type BoundingBox struct {
X float64 // Left edge in pixels
Y float64 // Top edge in pixels
Width float64 // Width in pixels
Height float64 // Height in pixels
}
The BoundingBox is derived from the element's content box model via DOM.getBoxModel. It may be nil for elements that are not rendered (e.g., hidden elements or elements with display: none).
WaitForSelector
WaitForSelector polls for an element to appear in the DOM. This is useful when waiting for dynamically loaded content:
// Wait for a loading spinner to disappear and content to appear
if err := wv.WaitForSelector(".content-loaded"); err != nil {
log.Fatal("content never appeared:", err)
}
// Now safe to query
elem, _ := wv.QuerySelector(".content-loaded h1")
The method polls every 100ms until the element is found or the timeout expires. The timeout is controlled by WithTimeout (default 30 seconds).
Getting Page Information
Several convenience methods retrieve page-level information without needing to write raw JavaScript:
// Current URL
url, err := wv.GetURL()
// Page title
title, err := wv.GetTitle()
// Outer HTML of an element (or full document if selector is empty)
html, err := wv.GetHTML(".article-body")
fullPage, err := wv.GetHTML("")
JavaScript Evaluation for Custom Queries
For queries beyond what CSS selectors can express, use Evaluate to run JavaScript directly:
// Count elements
result, err := wv.Evaluate("document.querySelectorAll('li.item').length")
if err == nil {
count := result.(float64)
log.Printf("Found %.0f items", count)
}
// Get computed style
result, err = wv.Evaluate(`
getComputedStyle(document.querySelector('.hero')).backgroundColor
`)
// Check visibility
result, err = wv.Evaluate(`
(function() {
const el = document.querySelector('#modal');
if (!el) return false;
const style = getComputedStyle(el);
return style.display !== 'none' && style.visibility !== 'hidden';
})()
`)
Evaluate uses the CDP Runtime.evaluate method with returnByValue: true, so the result is marshalled back as a Go value. JavaScript errors are caught and returned as Go errors.
Practical Examples
Extracting a Table
rows, err := wv.QuerySelectorAll("table#results tbody tr")
if err != nil {
log.Fatal(err)
}
for i, row := range rows {
// Use JavaScript to get cell text for each row
script := fmt.Sprintf(
"document.querySelectorAll('table#results tbody tr')[%d].innerText", i)
text, _ := wv.Evaluate(script)
log.Printf("Row %d: %v", i, text)
}
Checking if an Element Exists
elem, err := wv.QuerySelector(".error-banner")
if err != nil {
// Element does not exist -- no error banner is showing
log.Println("No errors on page")
} else {
log.Printf("Error banner found: %s", elem.Attributes["class"])
}
Waiting for Dynamic Content
wv.Navigate("https://example.com/dashboard")
// Wait for the chart to render
if err := wv.WaitForSelector("canvas.chart-rendered"); err != nil {
log.Fatal("chart did not render")
}
// Now capture a screenshot of the rendered page
data, _ := wv.Screenshot()
os.WriteFile("dashboard.png", data, 0644)
Next Steps
- Actions -- Interact with the elements you have found
- Console-Monitoring -- Watch for errors while querying
- Angular-Testing -- Use Angular-aware element queries