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