querySelectorAll: Find nodes that match a group of CSS selectors in an XML...

View source: R/main.R

querySelectorAllR Documentation

Find nodes that match a group of CSS selectors in an XML tree.

Description

The purpose of these functions is to mimic the functionality of the querySelector and querySelectorAll functions present in Internet browsers. This is so we can succinctly query an XML tree for nodes matching a CSS selector.

Namespaced functions querySelectorNS and querySelectorAllNS are also provided to search relative to a given namespace.

Usage

querySelector(doc, selector, ns = NULL, ...)
querySelectorAll(doc, selector, ns = NULL, ...)
querySelectorNS(doc, selector, ns,
                prefix = "descendant-or-self::", ...)
querySelectorAllNS(doc, selector, ns,
                   prefix = "descendant-or-self::", ...)

Arguments

doc

The XML document, node, or set of nodes to be evaluated against.

selector

A selector used to query doc. This must be a single character string.

ns

The namespaces that the query will be filtered to. This is a named list or vector whose name is the prefix a selector uses (the svg in "svg|g"), and whose value is the namespace URI that prefix stands for. Each name must be a valid XML NCName: a letter or _ followed by letters, digits, ., - or _, in any script, and no colon. This can be ignored for the un-namespaced functions, where a zero-length ns (character(0) or list()) additionally means no namespaces at all.

prefix

The prefix to apply to the resulting XPath expression. The default or "" are most commonly used.

...

Parameters to be passed onto css_to_xpath.

Details

The querySelectorNS and querySelectorAllNS functions are convenience functions for working with namespaced documents. They filter out all content that does not belong within the given namespaces. Note that when searching for particular elements in a selector, they must have a namespace prefix, e.g. "svg|g". The filter is relative to doc, so like the un-namespaced functions these search a node's own subtree rather than the whole document. A selector starting with :scope replaces the filter altogether (see below); such a selector is namespaced by its own prefixes, e.g. ":scope > svg|g".

The namespace argument, ns, is simply passed on to getNodeSet or xml_find_all if it is necessary to use a namespace present within the document. This can be ignored for content lacking a namespace, which is usually the case when using querySelector or querySelectorAll.

For querySelector and querySelectorAll, leaving ns as NULL on an xml2 document means the document's own namespace map is used, which xml_ns builds by walking the whole document on every query. Passing a zero-length ns, character(0) or list(), skips that lookup and queries with no namespaces, which is worth doing on a large document known to be un-namespaced. It is an error for the namespaced functions, which have nothing to filter to without a namespace.

A selector's bare element names match elements in no namespace, ":is(p)" and ":has(p)" exactly as "p" itself does, so an element in a default namespace has to be reached through a prefix. With xml2 the document's own prefixes are used when ns is not given, and xml_ns names a default namespace d1, making "d1|p" the selector for those elements.

Queries may be chained: as well as a document or a single node, doc may be a set of nodes, i.e. an xml2 xml_nodeset or an XML XMLNodeSet, as returned by querySelectorAll. The selector is then evaluated from each node of the set in turn, so a relative selector such as ":scope > a" applies per node. A node that matches from more than one node of the set is returned only once, at the position it first matched. An xml2 xml_missing (the result of a failed xml_find_first) is also accepted, and yields no matches rather than an error.

Selectors are translated with the generic (XML) translator unless a translator argument is given to be passed on to css_to_xpath, with one exception: a document parsed as HTML by htmlParse or read_html is queried with the html translator, so that element and attribute names are matched case-insensitively and the pseudo-classes that depend on HTML semantics (:checked, :disabled, :link, :lang() via the lang attribute, ...) work as they do in a browser. Passing translator explicitly overrides this for either kind of document.

The document is recognised however the query starts, so a chain of queries beginning at an HTML document keeps the html translator when it continues from one of the document's nodes or from a set of them.

A selector starting with the :scope pseudo-class is anchored at the queried node itself: querySelectorAll(node, ":scope > a") returns only the a children of node, where querySelectorAll(node, "a") would return all of its a descendants. :scope after a combinator or within a functional pseudo-class is an error (it cannot be expressed in XPath 1.0).

When doc is a whole document rather than a node, the queried node is taken to be the document's root element, so a bare :scope matches that root element and ":scope > x" matches its x children. This differs from a browser's document.querySelectorAll(), where :scope on a document refers to the document itself: a bare :scope matches nothing there (the document is not an element), while ":scope > html" matches the root element. To query starting from the root element itself rather than the document, pass the root node (e.g. xmlRoot or xml_root) as doc instead of the document.

Value

For querySelector, the result is a single node that represents the first matched node from a selector. If no matching nodes are found, NULL is returned.

For querySelectorAll, the result is a list of XML nodes. This list may be empty in the case that no match is found. The list is of the same type as the input document's package uses, so querying an xml_nodeset gives an xml_nodeset and querying an XMLNodeSet gives an XMLNodeSet.

The querySelectorNS and querySelectorAllNS functions return the same type of content as their un-namespaced counterparts.

Errors

These functions propagate the same selectr_parse_error and selectr_translation_error conditions that css_to_xpath raises for a malformed or unsupported selector (see ?css_to_xpath for their fields), plus selectr_argument_error for a bad R-level argument: doc that is not an XML or xml2 document, node, or node set; selector that is not a single character string; or ns that is not a named list or named character vector of non-empty strings whose names are valid XML names (or, for querySelectorNS and querySelectorAllNS, a missing or zero-length ns).

Author(s)

Simon Potter

References

CSS Selectors Level 4 https://www.w3.org/TR/selectors-4/, XPath https://www.w3.org/TR/xpath/, querySelectorAll https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll and https://dom.spec.whatwg.org/#dom-parentnode-queryselectorall.

See Also

css_to_xpath, whose ‘Errors’ section documents the condition classes' fields; selectors for the full selector-support reference.

Examples

  # All three selectr_error classes (see 'Errors' below) propagate
  # from these functions; selectr_argument_error also covers a 'doc'
  # that is not an XML or xml2 document, node, or node set.
  tryCatch(
    querySelectorAll("not a document", "a"),
    selectr_argument_error = function(e) cat(conditionMessage(e), "\n")
  )

  # The XML and xml2 packages are both optional (Suggests), so each demo
  # below is guarded with requireNamespace() and runs only when that
  # package is installed.

  # Demo for working with the XML package
  if (requireNamespace("XML", quietly = TRUE)) {
    exdoc <- XML::xmlParse('<a><b class="aclass"/><c id="anid"/></a>')
    querySelector(exdoc, "#anid")   # Returns the matching node
    querySelector(exdoc, ".aclass") # Returns the matching node
    querySelector(exdoc, "b, c")    # First match from grouped selection
    querySelectorAll(exdoc, "b, c") # Grouped selection
    querySelectorAll(exdoc, "b")    # A list of length one
    querySelector(exdoc, "d")       # No match
    querySelectorAll(exdoc, "d")    # No match

    # Queries can be chained, the second search running from each node
    # matched by the first
    querySelectorAll(querySelectorAll(exdoc, "a"), "c")

    # Read in a document where two namespaces are being set:
    # SVG and MathML
    svgdoc <- XML::xmlParse(system.file("demos/svg-mathml.svg",
                                        package = "selectr"))
    # Search for <script/> elements in the SVG namespace
    querySelectorNS(svgdoc, "svg|script",
                    c(svg = "http://www.w3.org/2000/svg"))
    querySelectorAllNS(svgdoc, "svg|script",
                       c(svg = "http://www.w3.org/2000/svg"))
    # MathML content is *within* SVG content,
    # search for <mtext> elements within the MathML namespace
    querySelectorNS(svgdoc, "math|mtext",
                    c(math = "http://www.w3.org/1998/Math/MathML"))
    querySelectorAllNS(svgdoc, "math|mtext",
                       c(math = "http://www.w3.org/1998/Math/MathML"))
    # Search for *both* SVG and MathML content
    querySelectorAllNS(svgdoc, "svg|script, math|mo",
                       c(svg = "http://www.w3.org/2000/svg",
                         math = "http://www.w3.org/1998/Math/MathML"))
  }

  # Demo for working with the xml2 package
  if (requireNamespace("xml2", quietly = TRUE)) {
    exdoc <- xml2::read_xml('<a><b class="aclass"/><c id="anid"/></a>')
    querySelector(exdoc, "#anid")   # Returns the matching node
    querySelector(exdoc, ".aclass") # Returns the matching node
    querySelector(exdoc, "b, c")    # First match from grouped selection
    querySelectorAll(exdoc, "b, c") # Grouped selection
    querySelectorAll(exdoc, "b")    # A nodeset of length one
    querySelector(exdoc, "d")       # No match
    querySelectorAll(exdoc, "d")    # No match

    # Queries can be chained, the second search running from each node
    # matched by the first
    querySelectorAll(querySelectorAll(exdoc, "a"), "c")

    # Read in a document where two namespaces are being set:
    # SVG and MathML
    svgdoc <- xml2::read_xml(system.file("demos/svg-mathml.svg",
                                         package = "selectr"))
    # Search for <script/> elements in the SVG namespace
    querySelectorNS(svgdoc, "svg|script",
                    c(svg = "http://www.w3.org/2000/svg"))
    querySelectorAllNS(svgdoc, "svg|script",
                       c(svg = "http://www.w3.org/2000/svg"))
    # MathML content is *within* SVG content,
    # search for <mtext> elements within the MathML namespace
    querySelectorNS(svgdoc, "math|mtext",
                    c(math = "http://www.w3.org/1998/Math/MathML"))
    querySelectorAllNS(svgdoc, "math|mtext",
                       c(math = "http://www.w3.org/1998/Math/MathML"))
    # Search for *both* SVG and MathML content
    querySelectorAllNS(svgdoc, "svg|script, math|mo",
                       c(svg = "http://www.w3.org/2000/svg",
                         math = "http://www.w3.org/1998/Math/MathML"))
  }

selectr documentation built on Sept. 17, 2026, 9:06 a.m.