--- title: "CSS selectors" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{CSS selectors} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") ``` zuhtml implements a deliberately small subset of CSS Selectors and rejects everything else with an error. A selector either works exactly as it would in a browser or fails loudly: it never matches something partly. ```{r} library(zuhtml) doc <- html_parse('

One

first

second

') ``` ## What is supported | Kind | Examples | |---|---| | Type and universal | `p`, `*` | | ID and class | `#main`, `.card.wide` | | Attributes | `[title]`, `[lang=en-US]`, `[class~=a]`, `[lang\|=en]`, `[href^=http]`, `[href$=".pdf"]`, `[href*=shop]`, with an `i` or `s` flag | | Combinators | `div p`, `div > p`, `h2 + p`, `h2 ~ p`, and lists: `h2, p` | | Pseudo-classes | `:scope`, `:root`, `:empty`, `:first-child`, `:last-child`, `:only-child`, `:nth-child(an+b)`, `:nth-of-type(an+b)`, `:not()` of one compound selector | ```{r} html_text(html_elements(doc, "h2 ~ p")) html_text(html_elements(doc, "li:nth-child(odd)")) html_text(html_elements(doc, "p:not([title])")) html_text(html_elements(doc, "[lang|=en]")) ``` ## What is rejected `:has()`, other pseudo-classes, pseudo-elements (including Scrapy's `::text` and `::attr()`), namespace prefixes and the `of S` form of `:nth-child()` are errors. The error says where: ```{r} try(html_elements(doc, "div:has(> h2)")) try(html_elements(doc, "p::text")) ``` Handle them by class, `zuhtml_selector_error`; the condition carries the selector, the position and whether the form is unsupported or malformed. ## Case As in a browser on an HTML page: element and attribute names match HTML elements whatever their case, and SVG or MathML elements exactly. IDs and classes are case-sensitive. Attribute values are case-sensitive, except for the attributes HTML lists as case-insensitive (`type`, `lang` and others); an `i` or `s` flag overrides either. ```{r} length(html_elements(doc, "P")) length(html_elements(doc, ".CARD")) length(html_elements(doc, "[lang=EN-US]")) length(html_elements(doc, "[lang=EN-US s]")) ``` ## Where the search looks Searching a document includes its `` element. Searching below an element excludes the element itself, unless `:scope` names it: ```{r} main <- html_element(doc, "#main") length(html_elements(main, ".card")) length(html_elements(main, ":scope.card")) html_name(html_elements(main, ":scope > *")) ``` The rest of a selector may match outside the context, as `querySelectorAll()` does: `"body h2"` below `#main` finds the `

` even though `` is outside it. `html_elements()` on several nodes returns the union of their matches, without duplicates, in document order. `html_element()` returns one result per input node, a missing node where there is no match: use it to pull one field out of each of many records. `