CSI is a complete spec-based CSS3 selector parser and utility written in JavaScript.
Features:
- Parse and tokenize any valid CSS3 selector string, guaranteed!
- Supports escaped characters, including escaped unicode code points
- Properly handles all valid whitespace
- Parses around comment blocks
- Validate that a selector is well-formed
- Calculate specificity level of each compound selector
- Sort theoretical property declarations in proper cascading order
based on selector specificity, origin (user or author stylesheet or inline), and
!important
directive - Normalize selector strings by parsing and reassembling the components in a deterministic manner
- Escape raw values to use as CSS identifiers or strings
Table of Contents
Install CSI into your project:
npm install css-selector-inspector --save
import CSI from 'css-selector-inspector';
console.log(CSI.parse('div.content:last-child > p:not([aria-hidden])::before'));
/* output:
[
{
tokens: [
{
type: "typeSelector",
namespace: null,
name: "div",
location: 0,
raw: "div",
specificityType: "d"
},
{
type: "classSelector",
name: "content",
location: 3,
raw: ".content",
specificityType: "c"
},
{
type: "pseudoClassSelector",
name: "last-child",
location: 11,
raw: ":last-child",
specificityType: "c",
expression: null
},
{
type: "childCombinator",
location: 22,
raw: " > ",
specificityType: null
},
{
type: "typeSelector",
namespace: null,
name: "p",
location: 25,
raw: "p",
specificityType: "d"
},
{
type: "negationSelector",
tokens: [
{
namespace: null,
name: "aria-hidden",
location: 31,
raw: "[aria-hidden]",
specificityType: "c",
type: "attributePresenceSelector"
}
],
location: 26,
raw: ":not([aria-hidden])",
specificityType: null
},
{
name: "before",
location: 45,
raw: "::before",
type: "pseudoElementSelector",
specificityType: "d"
}
],
specificity: {
a: 0,
b: 0,
c: 3,
d: 3
}
}
]
*/
console.log(CSI.parse('html, /* comments are ok! */ *|body'));
/* output:
[
{
tokens: [
{
type: "typeSelector",
namespace: null,
name: "html",
location: 0,
raw: "html",
specificityType: "d"
}
],
specificity: {
a: 0,
b: 0,
c: 0,
d: 1
}
},
{
tokens: [
{
type: "typeSelector",
namespace: {
type: "wildcard"
},
name: "body",
location: 29,
raw: "body",
specificityType: "d"
}
],
specificity: {
a: 0,
b: 0,
c: 0,
d: 1
}
}
]
This is the main object containing static methods for most of the selector utilities.
Parse a string containing selectors (one or more separated by commas) into its component tokens.
Params
- string
selector
- selector list string, excluding braces{}
Returns
array an array of Selector
objects (even if there is only one selector)
Throws
- Syntax error if the string cannot be parsed, usually providing the location of the first invalid portion
- If the parsing results in multiple, differing results, an ambiguity error is thrown (please file an issue if this error is encountered)
Determine if a selector string is syntactically valid. This is a convenience method if you don't care about the parsed result, and it will not throw an error if passed an invalid selector. This will not determine if the components are supported by a CSS implementation (e.g. if a particular pseudo class actually exists in any browser).
Params
- string
selector
- selector list string, excluding braces{}
Returns
bool determination of whether the selector string appears to be syntactically valid
Normalize a selector string by parsing and reassembling. Removes extraneous whitespace, unencodes unnecessary unicode escapes, removes comments, and normalizes nth formulas.
Params
- string
selector
- selector list string, excluding braces{}
Returns
string normalized selector string
Throws
- Uses
parse()
, so has the potential to throw errors due to invalid selector strings.
Sort selectors and/or theoretical properties based on the cascading precedence order. All arguments are internally
converted to TheoreticalProperty
objects for comparison purposes but are returned as the original passed object(s).
Selectors/properties are sorted in precedence order from highest to lowest, meaning the first item in the resulting
array is the property that would win out. For items that are otherwise equal based on precedence and specificity,
the item passed latest will appear first in the resulting array.
Params
- array
testObjects
| object...testObject
-Selector
orTheoreticalProperty
objects or plain objects to be casted asTheoreticalProperty
objects (can be passed as a single array of objects or as multiple object arguments)
Returns
array original passed objects in proper cascade order (does not mutate or return the original array if passed)
Throws
TheoreticalProperty
objects may throw errors if invalid values are passed. See its documentation for more details.
Escape a JS value to use as a CSS identity. This is an implementation of the CSS spec which defines the
CSS.escape()
method, available in many browsers.
Params
- mixed
ident
- raw value (will be casted as a string) to use as a CSS identity
Returns
string escaped identity that can be safely used to compose a CSS selector string
Escape a JS value to use as a CSS string. While escape()
can be used for this purpose as well, it escapes more characters
than necessary. This method only acts upon characters that must be escaped for use as a string, and returns the value with
surronding double quotes.
Params
- mixed
string
- raw value (will be casted as a string) to use as a CSS string
Returns
string escaped string, including surrounding double quote characters, that can be safely used to compose a CSS selector string
The Selector
object is returned as the result of parsing a selector string, which gives access to the token data
and specificity level. Selector
objects must be instantiated with a token array, so such objects typically
result from calling parse()
rather than direct instantiation.
Constructor
- array
tokens
- token objects
object contains properties a
, b
, c
, and d
, each with a count of the corresponding selectors of each specificity type
array array of token objects that comprise the compound selector, including combinators
Combinator Token Types
All combinator token objects have the following properties:
- string
type
- the type of token - int
location
- the index in the original string where the token was located - string
raw
- the raw string that was parsed into the token
Type | Example | Additional Properties |
---|---|---|
adjacentSiblingCombinator |
sibling + nextsibling |
|
childCombinator |
parent > child |
|
descendantCombinator |
ancestor descendant |
|
generalSiblingCombinator |
sibling ~ sibling |
|
Simple Selector Token Types
All simple selector token objects have the following properties:
- string
type
- the type of token - int
location
- the index in the original string where the token was located - string
raw
- the raw string that was parsed into the token
Type | Example | Additional Properties |
---|---|---|
attributePresenceSelector |
[attr] |
|
attributeValueSelector |
[attr="value"] |
|
classSelector |
.class |
|
idSelector |
#id |
|
negationSelector |
:not(.class) |
|
pseudoClassSelector |
:pseudo-class(expression) |
|
pseudoElementSelector |
::pseudo-element |
|
typeSelector |
element |
|
universalSelector |
* |
|
Data Token Types
All data token objects have the following properties:
- string
type
- the type of token - string
raw
- the raw string that was parsed into the token
Type | Example | Additional Properties |
---|---|---|
identity |
identity |
|
nthFormula |
3n+4 |
|
nthKeyword |
even |
|
string |
"string" |
|
Returns
string properly formed selector string
TheoreticalProperty
objects are used primarily to compare and sort different selectors. They are "theoretical"
properties because they are not concerned with any property name or value, but rather the source of the property
(e.g. user or author stylesheet or inline) and whether the !important
directive is set, all which contribute
to determining the cascading order of precedence of defined properties for an element.
The easiest way to use these objects is to not use them at all! Instead, you can pass Selector
objects
or plain objects to CSI.sort()
and the TheoreticalProperty
objects will be created under the hood
for the comparison. However, it is more efficient to create and reuse TheoreticalProperty
objects when
doing multiple/repeated comparisons.
Constructor
- object
options
| Selectorselector
| undefined - the constructor accepts a plain object which sets corresponding properties on theTheoreticalProperty
object; as a shortcut, aSelector
object may be passed instead, setting theselector
property to that object and theorigin
property to"author"
; if no argument is passed, the default properties are used, which make the object a non-important inline style
bool whether the property was defined with the !important
directive (properties originating from a userAgent
should not ever be important and will throw an error if used in a comparison)
Default: false
string where the CSS property originates from, one of: user
(configured in the user's browser settings),
userAgent
(defined by the browser itself), author
(defined in an external stylesheet or <style>
block),
or inline
(defined in an element's style=""
attribute)
Default: "inline"
Selector | null for CSS properties not originating from an inline style attribute, this should be set to a
Selector
object, otherwise this should be null
(if the expected value does not match the defined origin,
an error will be thrown upon comparison)
Default: null
Get the specificity of the selector/property. If the property's origin is a selector, the specificity object
comes directly from the Selector
object, otherwise it is always {a: 1, b: 0, c: 0, d: 0}
for inline styles.
Returns
object plain object with properties a
, b
, c
, d
, based on
how specificity is tallied
Throws
- An error is thrown if the
origin
is invalid or does not match the expected value ofselector
Based on the origin of the property and whether it is !important
, determine the precendence level.
A lower value indicates a higher precedence.
Returns
int precedence value, lower being higher precedence
Throws
- An error is thrown if the origin type is invalid.
- This package uses a number methods introduced by ECMAScript 2015 which may not be available in all environments. You may need to use a polyfill for the following methods:
- The parsing functionality is built upon nearley, a JS implementation of the Earley Parsing Algorithm. I was introduced to nearley and inspired to use it for the purposes of CSS selector parsing by scalpel.
- After evaluating several CSS selector parsers, my goal was to produce one that could handle any valid selector string based on the spec. As such, please file an issue if you come across any valid selector strings that cannot be parsed!