css-selector-parser
Advanced tools
Changelog
3.0.0 (2023-09-26)
Rule.tag
was moved to Rule.items
.
Example selector: div
.
{type: 'Rule', tagName: {type: 'TagName', name: 'div'}}
{type: 'Rule', items: [{type: 'TagName', name: 'div'}]}
Rule.classNames
was converted to an AST entity and moved to Rule.items
.
Example selector: .user.hidden
{type: 'Rule', classNames: ['user', 'hidden']}
{type: 'Rule', items: [{type: 'ClassName', name: 'user'}, {type: 'ClassName', name: 'hidden'}]}
Rule.ids
was converted to an AST entity and moved to Rule.items
.
Example selector: #root#user-1
{type: 'Rule', ids: ['root', 'user-1']}
{type: 'Rule', items: [{type: 'Id', name: 'root'}, {type: 'Id', name: 'user-1'}]}
Rule.attributes
was moved to Rule.items
.
Example selector: [href^=/][role]
{type: 'Rule', attributes: [{type: 'Attribute', name: 'href', operator: '^=', value: {type: 'String', value: '/'}}, {type: 'Attribute', name: 'role'}]}
{type: 'Rule', items: [{type: 'Attribute', name: 'href', operator: '^=', value: {type: 'String', value: '/'}}, {type: 'Attribute', name: 'role'}]}
Rule.pseudoClasses
was moved to Rule.items
.
Example selector: :hover:lang(en)
{type: 'Rule', pseudoClasses: [{type: 'PseudoClass', name: 'hover'}, {type: 'PseudoClass', name: 'lang', value: {type: 'String', value: 'en'}}]}
{type: 'Rule', items: [{type: 'PseudoClass', name: 'hover'}, {type: 'PseudoClass', name: 'lang', value: {type: 'String', value: 'en'}}]}
Rule.pseudoElement
was converted to an AST entity and moved to Rule.items
.
Example selector: ::before
{type: 'Rule', pseudoElement: 'before'}
{type: 'Rule', items: [{type: 'PseudoElement', name: 'before'}]}
ast.id
and ast.isId
to create and test ast nodes with type Id
.ast.className
and ast.isClassName
to create and test ast nodes with type ClassName
.ast.pseudoElement
and ast.isPseudoElement
to create and test ast nodes with type PseudoElement
.pseudoElements.definitions
was updated to accept signatures in otder to support specifying pseudo-elements with
an argument.
Example: createParser({syntax: {pseudoElements: {definitions: {NoArgument: ['before'], String: ['highlight'], Selector: ['slotted']}}}})
.CssSelectorParser
-> createParser
In 1.x versions there was CssSelectorParser
class which had to be contructed and then configured.
In 3.x versions there is createParser()
function which returns a parse()
function. All the configutation is passed
to createParser()
params.
Before:
var CssSelectorParser = require('css-selector-parser').CssSelectorParser,
parser = new CssSelectorParser();
parser.registerSelectorPseudos('has');
parser.registerNumericPseudos('nth-child');
parser.registerNestingOperators('>', '+', '~');
parser.registerAttrEqualityMods('^', '$', '*', '~');
const selector = parser.parse('a[href^=/], .container:has(nav) > a[href]:lt($var):nth-child(5)');
After:
import {createParser} from 'css-selector-parser';
const parse = createParser({
syntax: {
pseudoClasses: {
// In 1.x any pseudo-classes were accepted.
// in 2.x parser only accepts known psuedo-classes unless `unknown: accept` was specified.
unknown: 'accept',
definitions: {
// This is a replacement for registerSelectorPseudos().
Selector: ['has'],
// This is a replacement for registerNumericPseudos().
Formula: ['nth-child']
}
},
// This is a replacement for registerNestingOperators().
combinators: ['>', '+', '~'],
attributes: {
// This a replacement for registerAttrEqualityMods().
// Note that equals sign ("=") is included into the operator definitions.
operators: ['^=', '$=', '*=', '~=']
}
},
// This is a replacement for enableSubstitutes()
substitutes: true
});
const selector = parse('a[href^=/], .container:has(nav) > a[href]:lt($var):nth-child(5)');
You no longer need to make an extensive configuration of css-selector-parser
in order to make it understand
the necessary CSS standards. You can now just define CSS/CSS selectors version directly:
import {createParser} from 'css-selector-parser';
const parse = createParser({syntax: 'css3'});
const selector = parse('a[href^=/], .container:has(nav) > a[href]:nth-child(2n + 1)::before');
Here are the pre-defined CSS standards for your disposal:
css1
: https://www.w3.org/TR/CSS1/css2
: https://www.w3.org/TR/CSS2/css3
/selectors-3
: https://www.w3.org/TR/selectors-3/selectors-4
: https://www.w3.org/TR/selectors-4/latest
: refers to selectors-4
progressive
: latest
+ accepts unknown psudo-classes, psudo-elements and attribute case sensitivity modifiersstrict
valueCSS selector parser in modern browsers is very forgiving. For instance, it works fine with unclosed attribute
selectors: "[attr=value"
. If you would like to mimic this behavior from browsers, set strict
to false
, i.e.:
import {createParser} from 'css-selector-parser';
const parse = createParser({syntax: 'css3', strict: false});
const selector = parse(':lang(en'); // doesn't crash
render()
method used to be a method of CssSelectorParser
class. Now it can be imported directly and used.
Example:
import {createParser, render} from 'css-selector-parser';
const parse = createParser({syntax: 'progressive'});
const selector = parse('div#user-123.user:lang(en)::before');
console.log(render(selector)); // div#user-123.user:lang(en)::before
AST had a lot of changes.
selector
-> Selector
.selectors
-> rules
, also selectors
contained ruleSet[]
, which in turn has rule
field,
and new rules
contains Rule[]
directly.Before: {type: 'selector', selectors: [ {type: 'ruleSet', rule: {<RULE 1 DATA>}}, {type: 'ruleSet', rule: {<RULE 2 DATA>}} ]}
.
After: {type: 'Selector', rules: [ {<RULE 1 DATA>}, {<RULE 2 DATA>} ]}
.
rule
-> Rule
.id: string
-> items: [{type: 'Id', name: '<ID>'}, ...]
. According to the CSS spec one rule may have
more than 1 id
, so #root#root
is a valid selector.nestingOperator
-> combinator
. A proper name according to CSS spec was chosen.rule
-> nestedRule
. A proper name to indicate nesting was chosen.tagName: string
-> items: [TagName | WildcardTag, ...]
. Using explicit distinction between
TagName (i.e. div
) and WildcardTag (*
), because tag name can also be *
if escaped properly (\*
).attrs
-> items: [<ATTRIBUTE>, ...]
. Attribute type was changed, see below.pseudos
-> items: [<PSEUDO CLASS>, ...]
. There are pseudo-elements and pseudo-classes, now they are
separated properly (there is a separate pseudoElement
type). Pseudo class type was changed, see below.Before:
({
type: 'rule',
tagName: 'div',
id: 'user-123',
classNames: ['user'],
attrs: [
{name: 'role', operator: '$=', valueType: 'string', value: 'button'}
],
pseudos: [
{name: 'lang', valueType: 'string', value: 'en'}
],
nestingOperator: '>'
})
After:
({
type: 'Rule',
items: [
{type: 'TagName', name: 'div'},
{type: 'Id', name: 'user-123'},
{type: 'ClassName', name: 'user'},
{type: 'Attribute', name: 'role', operator: '$=', value: {type: 'String', value: 'button'}},
{type: 'PseudoClass', name: 'lang', value: {type: 'String', value: 'en'}}
],
combinator: '>'
})
Attribute
.value
and valueType
were combined to a single prop value
with a field type
.Before: {name: 'role'}
.
After: {type: 'Attribute', name: 'role'}
.
Before: {name: 'role', operator: '$=', valueType: 'string', value: 'button'}
.
After: {type: 'Attribute', name: 'role', operator: '$=', value: {type: 'String', value: 'button'}}
.
Before: {name: 'role', operator: '=', valueType: 'substitute', value: 'var'}
.
After: {type: 'Attribute', name: 'role', operator: '=', value: {type: 'Substitute', name: 'var'}}
.
PseudoClass
.value
and valueType
were combined to a single prop argument
with a field type
.Before: {name: 'visited'}
.
After: {type: 'PseudoClass', name: 'visited'}
.
Before: {name: 'lang', valueType: 'string', value: 'en'}
.
After: {type: 'PseudoClass', name: 'lang', argument: {type: 'String', value: 'en'}}
.
Before: {name: 'lang', valueType: 'substitute', value: 'var'}
.
After: {type: 'PseudoClass', name: 'lang', argument: {type: 'Substitute', name: 'var'}}
.
Before: {name: 'has', valueType: 'selector', value: {type: 'selector', selectors: [{type: 'ruleSet', rule: {type: 'rule', tagName: 'div'}}]}}
.
After: {type: 'PseudoClass', name: 'has', argument: {type: 'Selector', rules: [{type: 'Rule', tag: {type: 'TagName', name: 'div'}}]}}
.
PseudoElement
.