[79a0317] | 1 | # css-select [![NPM version](http://img.shields.io/npm/v/css-select.svg)](https://npmjs.org/package/css-select) [![Build Status](https://travis-ci.com/fb55/css-select.svg?branch=master)](http://travis-ci.com/fb55/css-select) [![Downloads](https://img.shields.io/npm/dm/css-select.svg)](https://npmjs.org/package/css-select) [![Coverage](https://coveralls.io/repos/fb55/css-select/badge.svg?branch=master)](https://coveralls.io/r/fb55/css-select)
|
---|
| 2 |
|
---|
| 3 | A CSS selector compiler and engine
|
---|
| 4 |
|
---|
| 5 | ## What?
|
---|
| 6 |
|
---|
| 7 | As a **compiler**, css-select turns CSS selectors into functions that tests if
|
---|
| 8 | elements match them.
|
---|
| 9 |
|
---|
| 10 | As an **engine**, css-select looks through a DOM tree, searching for elements.
|
---|
| 11 | Elements are tested "from the top", similar to how browsers execute CSS
|
---|
| 12 | selectors.
|
---|
| 13 |
|
---|
| 14 | In its default configuration, css-select queries the DOM structure of the
|
---|
| 15 | [`domhandler`](https://github.com/fb55/domhandler) module (also known as
|
---|
| 16 | htmlparser2 DOM). To query alternative DOM structures, see [`Options`](#options)
|
---|
| 17 | below.
|
---|
| 18 |
|
---|
| 19 | **Features:**
|
---|
| 20 |
|
---|
| 21 | - ๐ฌ Full implementation of CSS3 selectors, as well as most CSS4 selectors
|
---|
| 22 | - ๐งช Partial implementation of jQuery/Sizzle extensions (see
|
---|
| 23 | [cheerio-select](https://github.com/cheeriojs/cheerio-select) for the
|
---|
| 24 | remaining selectors)
|
---|
| 25 | - ๐งโ๐ฌ High test coverage, including the full test suites from Sizzle, Qwery and
|
---|
| 26 | NWMatcher.
|
---|
| 27 | - ๐ฅผ Reliably great performance
|
---|
| 28 |
|
---|
| 29 | ## Why?
|
---|
| 30 |
|
---|
| 31 | Most CSS engines written in JavaScript execute selectors left-to-right. That
|
---|
| 32 | means thet execute every component of the selector in order, from left to right
|
---|
| 33 | _(duh)_. As an example: For the selector `a b`, these engines will first query
|
---|
| 34 | for `a` elements, then search these for `b` elements. (That's the approach of
|
---|
| 35 | eg. [`Sizzle`](https://github.com/jquery/sizzle),
|
---|
| 36 | [`nwmatcher`](https://github.com/dperini/nwmatcher/) and
|
---|
| 37 | [`qwery`](https://github.com/ded/qwery).)
|
---|
| 38 |
|
---|
| 39 | While this works, it has some downsides: Children of `a`s will be checked
|
---|
| 40 | multiple times; first, to check if they are also `a`s, then, for every superior
|
---|
| 41 | `a` once, if they are `b`s. Using
|
---|
| 42 | [Big O notation](http://en.wikipedia.org/wiki/Big_O_notation), that would be
|
---|
| 43 | `O(n^(k+1))`, where `k` is the number of descendant selectors (that's the space
|
---|
| 44 | in the example above).
|
---|
| 45 |
|
---|
| 46 | The far more efficient approach is to first look for `b` elements, then check if
|
---|
| 47 | they have superior `a` elements: Using big O notation again, that would be
|
---|
| 48 | `O(n)`. That's called right-to-left execution.
|
---|
| 49 |
|
---|
| 50 | And that's what css-select does โ and why it's quite performant.
|
---|
| 51 |
|
---|
| 52 | ## How does it work?
|
---|
| 53 |
|
---|
| 54 | By building a stack of functions.
|
---|
| 55 |
|
---|
| 56 | _Wait, what?_
|
---|
| 57 |
|
---|
| 58 | Okay, so let's suppose we want to compile the selector `a b`, for right-to-left
|
---|
| 59 | execution. We start by _parsing_ the selector. This turns the selector into an
|
---|
| 60 | array of the building blocks. That's what the
|
---|
| 61 | [`css-what`](https://github.com/fb55/css-what) module is for, if you want to
|
---|
| 62 | have a look.
|
---|
| 63 |
|
---|
| 64 | Anyway, after parsing, we end up with an array like this one:
|
---|
| 65 |
|
---|
| 66 | ```js
|
---|
| 67 | [
|
---|
| 68 | { type: "tag", name: "a" },
|
---|
| 69 | { type: "descendant" },
|
---|
| 70 | { type: "tag", name: "b" },
|
---|
| 71 | ];
|
---|
| 72 | ```
|
---|
| 73 |
|
---|
| 74 | (Actually, this array is wrapped in another array, but that's another story,
|
---|
| 75 | involving commas in selectors.)
|
---|
| 76 |
|
---|
| 77 | Now that we know the meaning of every part of the selector, we can compile it.
|
---|
| 78 | That is where things become interesting.
|
---|
| 79 |
|
---|
| 80 | The basic idea is to turn every part of the selector into a function, which
|
---|
| 81 | takes an element as its only argument. The function checks whether a passed
|
---|
| 82 | element matches its part of the selector: If it does, the element is passed to
|
---|
| 83 | the next function representing the next part of the selector. That function does
|
---|
| 84 | the same. If an element is accepted by all parts of the selector, it _matches_
|
---|
| 85 | the selector and double rainbow ALL THE WAY.
|
---|
| 86 |
|
---|
| 87 | As said before, we want to do right-to-left execution with all the big O
|
---|
| 88 | improvements. That means elements are passed from the rightmost part of the
|
---|
| 89 | selector (`b` in our example) to the leftmost (~~which would be `c`~~ of course
|
---|
| 90 | `a`).
|
---|
| 91 |
|
---|
| 92 | For traversals, such as the _descendant_ operating the space between `a` and
|
---|
| 93 | `b`, we walk up the DOM tree, starting from the element passed as argument.
|
---|
| 94 |
|
---|
| 95 | _//TODO: More in-depth description. Implementation details. Build a spaceship._
|
---|
| 96 |
|
---|
| 97 | ## API
|
---|
| 98 |
|
---|
| 99 | ```js
|
---|
| 100 | const CSSselect = require("css-select");
|
---|
| 101 | ```
|
---|
| 102 |
|
---|
| 103 | **Note:** css-select throws errors when invalid selectors are passed to it.This
|
---|
| 104 | is done to aid with writing css selectors, but can be unexpected when processing
|
---|
| 105 | arbitrary strings.
|
---|
| 106 |
|
---|
| 107 | #### `CSSselect.selectAll(query, elems, options)`
|
---|
| 108 |
|
---|
| 109 | Queries `elems`, returns an array containing all matches.
|
---|
| 110 |
|
---|
| 111 | - `query` can be either a CSS selector or a function.
|
---|
| 112 | - `elems` can be either an array of elements, or a single element. If it is an
|
---|
| 113 | element, its children will be queried.
|
---|
| 114 | - `options` is described below.
|
---|
| 115 |
|
---|
| 116 | Aliases: `default` export, `CSSselect.iterate(query, elems)`.
|
---|
| 117 |
|
---|
| 118 | #### `CSSselect.compile(query, options)`
|
---|
| 119 |
|
---|
| 120 | Compiles the query, returns a function.
|
---|
| 121 |
|
---|
| 122 | #### `CSSselect.is(elem, query, options)`
|
---|
| 123 |
|
---|
| 124 | Tests whether or not an element is matched by `query`. `query` can be either a
|
---|
| 125 | CSS selector or a function.
|
---|
| 126 |
|
---|
| 127 | #### `CSSselect.selectOne(query, elems, options)`
|
---|
| 128 |
|
---|
| 129 | Arguments are the same as for `CSSselect.selectAll(query, elems)`. Only returns
|
---|
| 130 | the first match, or `null` if there was no match.
|
---|
| 131 |
|
---|
| 132 | ### Options
|
---|
| 133 |
|
---|
| 134 | All options are optional.
|
---|
| 135 |
|
---|
| 136 | - `xmlMode`: When enabled, tag names will be case-sensitive. Default: `false`.
|
---|
| 137 | - `rootFunc`: The last function in the stack, will be called with the last
|
---|
| 138 | element that's looked at.
|
---|
| 139 | - `adapter`: The adapter to use when interacting with the backing DOM
|
---|
| 140 | structure. By default it uses the `domutils` module.
|
---|
| 141 | - `context`: The context of the current query. Used to limit the scope of
|
---|
| 142 | searches. Can be matched directly using the `:scope` pseudo-selector.
|
---|
| 143 | - `cacheResults`: Allow css-select to cache results for some selectors,
|
---|
| 144 | sometimes greatly improving querying performance. Disable this if your
|
---|
| 145 | document can change in between queries with the same compiled selector.
|
---|
| 146 | Default: `true`.
|
---|
| 147 |
|
---|
| 148 | #### Custom Adapters
|
---|
| 149 |
|
---|
| 150 | A custom adapter must match the interface described
|
---|
| 151 | [here](https://github.com/fb55/css-select/blob/1aa44bdd64aaf2ebdfd7f338e2e76bed36521957/src/types.ts#L6-L96).
|
---|
| 152 |
|
---|
| 153 | You may want to have a look at [`domutils`](https://github.com/fb55/domutils) to
|
---|
| 154 | see the default implementation, or at
|
---|
| 155 | [`css-select-browser-adapter`](https://github.com/nrkn/css-select-browser-adapter/blob/master/index.js)
|
---|
| 156 | for an implementation backed by the DOM.
|
---|
| 157 |
|
---|
| 158 | ## Supported selectors
|
---|
| 159 |
|
---|
| 160 | _As defined by CSS 4 and / or jQuery._
|
---|
| 161 |
|
---|
| 162 | - [Selector lists](https://developer.mozilla.org/en-US/docs/Web/CSS/Selector_list)
|
---|
| 163 | (`,`)
|
---|
| 164 | - [Universal](https://developer.mozilla.org/en-US/docs/Web/CSS/Universal_selectors)
|
---|
| 165 | (`*`)
|
---|
| 166 | - [Type](https://developer.mozilla.org/en-US/docs/Web/CSS/Type_selectors)
|
---|
| 167 | (`<tagname>`)
|
---|
| 168 | - [Descendant](https://developer.mozilla.org/en-US/docs/Web/CSS/Descendant_combinator)
|
---|
| 169 | (` `)
|
---|
| 170 | - [Child](https://developer.mozilla.org/en-US/docs/Web/CSS/Child_combinator)
|
---|
| 171 | (`>`)
|
---|
| 172 | - Parent (`<`)
|
---|
| 173 | - [Adjacent sibling](https://developer.mozilla.org/en-US/docs/Web/CSS/Adjacent_sibling_combinator)
|
---|
| 174 | (`+`)
|
---|
| 175 | - [General sibling](https://developer.mozilla.org/en-US/docs/Web/CSS/General_sibling_combinator)
|
---|
| 176 | (`~`)
|
---|
| 177 | - [Attribute](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors)
|
---|
| 178 | (`[attr=foo]`), with supported comparisons:
|
---|
| 179 | - `[attr]` (existential)
|
---|
| 180 | - `=`
|
---|
| 181 | - `~=`
|
---|
| 182 | - `|=`
|
---|
| 183 | - `*=`
|
---|
| 184 | - `^=`
|
---|
| 185 | - `$=`
|
---|
| 186 | - `!=`
|
---|
| 187 | - `i` and `s` can be added after the comparison to make the comparison
|
---|
| 188 | case-insensitive or case-sensitive (eg. `[attr=foo i]`). If neither is
|
---|
| 189 | supplied, css-select will follow the HTML spec's
|
---|
| 190 | [case-sensitivity rules](https://html.spec.whatwg.org/multipage/semantics-other.html#case-sensitivity-of-selectors).
|
---|
| 191 | - Pseudos:
|
---|
| 192 | - [`:not`](https://developer.mozilla.org/en-US/docs/Web/CSS/:not)
|
---|
| 193 | - [`:contains`](https://api.jquery.com/contains-selector)
|
---|
| 194 | - `:icontains` (case-insensitive version of `:contains`)
|
---|
| 195 | - [`:has`](https://developer.mozilla.org/en-US/docs/Web/CSS/:has)
|
---|
| 196 | - [`:root`](https://developer.mozilla.org/en-US/docs/Web/CSS/:root)
|
---|
| 197 | - [`:empty`](https://developer.mozilla.org/en-US/docs/Web/CSS/:empty)
|
---|
| 198 | - [`:parent`](https://api.jquery.com/parent-selector)
|
---|
| 199 | - [`:first-child`](https://developer.mozilla.org/en-US/docs/Web/CSS/:first-child),
|
---|
| 200 | [`:last-child`](https://developer.mozilla.org/en-US/docs/Web/CSS/:last-child),
|
---|
| 201 | [`:first-of-type`](https://developer.mozilla.org/en-US/docs/Web/CSS/:first-of-type),
|
---|
| 202 | [`:last-of-type`](https://developer.mozilla.org/en-US/docs/Web/CSS/:last-of-type)
|
---|
| 203 | - [`:only-of-type`](https://developer.mozilla.org/en-US/docs/Web/CSS/:only-of-type),
|
---|
| 204 | [`:only-child`](https://developer.mozilla.org/en-US/docs/Web/CSS/:only-child)
|
---|
| 205 | - [`:nth-child`](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-child),
|
---|
| 206 | [`:nth-last-child`](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-last-child),
|
---|
| 207 | [`:nth-of-type`](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-of-type),
|
---|
| 208 | [`:nth-last-of-type`](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-last-of-type),
|
---|
| 209 | - [`:link`](https://developer.mozilla.org/en-US/docs/Web/CSS/:link),
|
---|
| 210 | [`:any-link`](https://developer.mozilla.org/en-US/docs/Web/CSS/:any-link)
|
---|
| 211 | - [`:visited`](https://developer.mozilla.org/en-US/docs/Web/CSS/:visited),
|
---|
| 212 | [`:hover`](https://developer.mozilla.org/en-US/docs/Web/CSS/:hover),
|
---|
| 213 | [`:active`](https://developer.mozilla.org/en-US/docs/Web/CSS/:active)
|
---|
| 214 | (these depend on optional `Adapter` methods, so these will only match
|
---|
| 215 | elements if implemented in `Adapter`)
|
---|
| 216 | - [`:selected`](https://api.jquery.com/selected-selector),
|
---|
| 217 | [`:checked`](https://developer.mozilla.org/en-US/docs/Web/CSS/:checked)
|
---|
| 218 | - [`:enabled`](https://developer.mozilla.org/en-US/docs/Web/CSS/:enabled),
|
---|
| 219 | [`:disabled`](https://developer.mozilla.org/en-US/docs/Web/CSS/:disabled)
|
---|
| 220 | - [`:required`](https://developer.mozilla.org/en-US/docs/Web/CSS/:required),
|
---|
| 221 | [`:optional`](https://developer.mozilla.org/en-US/docs/Web/CSS/:optional)
|
---|
| 222 | - [`:header`](https://api.jquery.com/header-selector),
|
---|
| 223 | [`:button`](https://api.jquery.com/button-selector),
|
---|
| 224 | [`:input`](https://api.jquery.com/input-selector),
|
---|
| 225 | [`:text`](https://api.jquery.com/text-selector),
|
---|
| 226 | [`:checkbox`](https://api.jquery.com/checkbox-selector),
|
---|
| 227 | [`:file`](https://api.jquery.com/file-selector),
|
---|
| 228 | [`:password`](https://api.jquery.com/password-selector),
|
---|
| 229 | [`:reset`](https://api.jquery.com/reset-selector),
|
---|
| 230 | [`:radio`](https://api.jquery.com/radio-selector) etc.
|
---|
| 231 | - [`:is`](https://developer.mozilla.org/en-US/docs/Web/CSS/:is), plus its
|
---|
| 232 | legacy alias `:matches`
|
---|
| 233 | - [`:scope`](https://developer.mozilla.org/en-US/docs/Web/CSS/:scope)
|
---|
| 234 | (uses the context from the passed options)
|
---|
| 235 |
|
---|
| 236 | ---
|
---|
| 237 |
|
---|
| 238 | License: BSD-2-Clause
|
---|
| 239 |
|
---|
| 240 | ## Security contact information
|
---|
| 241 |
|
---|
| 242 | To report a security vulnerability, please use the
|
---|
| 243 | [Tidelift security contact](https://tidelift.com/security). Tidelift will
|
---|
| 244 | coordinate the fix and disclosure.
|
---|
| 245 |
|
---|
| 246 | ## `css-select` for enterprise
|
---|
| 247 |
|
---|
| 248 | Available as part of the Tidelift Subscription
|
---|
| 249 |
|
---|
| 250 | The maintainers of `css-select` and thousands of other packages are working with
|
---|
| 251 | Tidelift to deliver commercial support and maintenance for the open source
|
---|
| 252 | dependencies you use to build your applications. Save time, reduce risk, and
|
---|
| 253 | improve code health, while paying the maintainers of the exact dependencies you
|
---|
| 254 | use.
|
---|
| 255 | [Learn more.](https://tidelift.com/subscription/pkg/npm-css-select?utm_source=npm-css-select&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
|
---|