Security News
Input Validation Vulnerabilities Dominate MITRE's 2024 CWE Top 25 List
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
@asamuzakjp/dom-selector
Advanced tools
A CSS selector engine.
npm i @asamuzakjp/dom-selector
import { DOMSelector } from '@asamuzakjp/dom-selector';
import { JSDOM } from 'jsdom';
const { window } = new JSDOM();
const {
closest, matches, querySelector, querySelectorAll
} = new DOMSelector(window);
matches - equivalent to Element.matches()
Returns boolean true
if matched, false
otherwise
closest - equivalent to Element.closest()
Returns object? matched node
querySelector - equivalent to Document.querySelector(), DocumentFragment.querySelector() and Element.querySelector()
selector
string CSS selectornode
object Document, DocumentFragment or Element nodeopt
object? options
Returns object? matched node
querySelectorAll - equivalent to Document.querySelectorAll(), DocumentFragment.querySelectorAll() and Element.querySelectorAll()
NOTE: returns Array, not NodeList
selector
string CSS selectornode
object Document, DocumentFragment or Element nodeopt
object? options
Returns Array<(object | undefined)> array of matched nodes
Pattern | Supported | Note |
---|---|---|
* | ✓ | |
ns|E | ✓ | |
*|E | ✓ | |
|E | ✓ | |
E | ✓ | |
E:not(s1, s2, …) | ✓ | |
E:is(s1, s2, …) | ✓ | |
E:where(s1, s2, …) | ✓ | |
E:has(rs1, rs2, …) | ✓ | |
E.warning | ✓ | |
E#myid | ✓ | |
E[foo] | ✓ | |
E[foo="bar"] | ✓ | |
E[foo="bar" i] | ✓ | |
E[foo="bar" s] | ✓ | |
E[foo~="bar"] | ✓ | |
E[foo^="bar"] | ✓ | |
E[foo$="bar"] | ✓ | |
E[foo*="bar"] | ✓ | |
E[foo|="en"] | ✓ | |
E:defined | Partially supported | Matching with MathML is not yet supported. |
E:dir(ltr) | ✓ | |
E:lang(en) | Partially supported | Comma-separated list of language codes, e.g. :lang(en, fr) , is not yet supported. |
E:any‑link | ✓ | |
E:link | ✓ | |
E:visited | ✓ | Returns false or null to prevent fingerprinting. |
E:local‑link | ✓ | |
E:target | ✓ | |
E:target‑within | ✓ | |
E:scope | ✓ | |
E:current | Unsupported | |
E:current(s) | Unsupported | |
E:past | Unsupported | |
E:future | Unsupported | |
E:active | ✓ | |
E:hover | ✓ | |
E:focus | ✓ | |
E:focus‑within | ✓ | |
E:focus‑visible | ✓ | |
E:open E:closed | Partially supported | Matching with <select>, e.g. select:open , is not supported. |
E:enabled E:disabled | ✓ | |
E:read‑write E:read‑only | ✓ | |
E:placeholder‑shown | ✓ | |
E:default | ✓ | |
E:checked | ✓ | |
E:indeterminate | ✓ | |
E:valid E:invalid | ✓ | |
E:required E:optional | ✓ | |
E:blank | Unsupported | |
E:user‑valid E:user‑invalid | Unsupported | |
E:root | ✓ | |
E:empty | ✓ | |
E:nth‑child(n [of S]?) | ✓ | |
E:nth‑last‑child(n [of S]?) | ✓ | |
E:first‑child | ✓ | |
E:last‑child | ✓ | |
E:only‑child | ✓ | |
E:nth‑of‑type(n) | ✓ | |
E:nth‑last‑of‑type(n) | ✓ | |
E:first‑of‑type | ✓ | |
E:last‑of‑type | ✓ | |
E:only‑of‑type | ✓ | |
E F | ✓ | |
E > F | ✓ | |
E + F | ✓ | |
E ~ F | ✓ | |
F || E | Unsupported | |
E:nth‑col(n) | Unsupported | |
E:nth‑last‑col(n) | Unsupported | |
E:host | ✓ | |
E:host(s) | ✓ | |
E:host‑context(s) | ✓ | |
E:popover-open | ✓ | |
E:state(v) | ✓ | *1 |
E:host(:state(v)) | ✓ | *1 |
*1: ElementInternals.states
, i.e. CustomStateSet
, is not implemented in jsdom, so you need to apply a patch in the custom element constructor.
class LabeledCheckbox extends window.HTMLElement {
#internals;
constructor() {
super();
this.#internals = this.attachInternals();
// patch CustomStateSet
if (!this.#internals.states) {
this.#internals.states = new Set();
}
this.addEventListener('click', this._onClick.bind(this));
}
get checked() {
return this.#internals.states.has('checked');
}
set checked(flag) {
if (flag) {
this.#internals.states.add('checked');
} else {
this.#internals.states.delete('checked');
}
}
_onClick(event) {
this.checked = !this.checked;
}
}
import { DOMSelector } from '@asamuzakjp/dom-selector';
import { JSDOM } from 'jsdom';
const dom = new JSDOM('', {
runScripts: 'dangerously',
url: 'http://localhost/',
beforeParse: window => {
const domSelector = new DOMSelector(window);
const matches = domSelector.matches.bind(domSelector);
window.Element.prototype.matches = function (...args) {
if (!args.length) {
throw new window.TypeError('1 argument required, but only 0 present.');
}
const [selector] = args;
return matches(selector, this);
};
const closest = domSelector.closest.bind(domSelector);
window.Element.prototype.closest = function (...args) {
if (!args.length) {
throw new window.TypeError('1 argument required, but only 0 present.');
}
const [selector] = args;
return closest(selector, this);
};
const querySelector = domSelector.querySelector.bind(domSelector);
window.Document.prototype.querySelector = function (...args) {
if (!args.length) {
throw new window.TypeError('1 argument required, but only 0 present.');
}
const [selector] = args;
return querySelector(selector, this);
};
window.DocumentFragment.prototype.querySelector = function (...args) {
if (!args.length) {
throw new window.TypeError('1 argument required, but only 0 present.');
}
const [selector] = args;
return querySelector(selector, this);
};
window.Element.prototype.querySelector = function (...args) {
if (!args.length) {
throw new window.TypeError('1 argument required, but only 0 present.');
}
const [selector] = args;
return querySelector(selector, this);
};
const querySelectorAll = domSelector.querySelectorAll.bind(domSelector);
window.Document.prototype.querySelectorAll = function (...args) {
if (!args.length) {
throw new window.TypeError('1 argument required, but only 0 present.');
}
const [selector] = args;
return querySelectorAll(selector, this);
};
window.DocumentFragment.prototype.querySelectorAll = function (...args) {
if (!args.length) {
throw new window.TypeError('1 argument required, but only 0 present.');
}
const [selector] = args;
return querySelectorAll(selector, this);
};
window.Element.prototype.querySelectorAll = function (...args) {
if (!args.length) {
throw new window.TypeError('1 argument required, but only 0 present.');
}
const [selector] = args;
return querySelectorAll(selector, this);
};
}
});
See benchmark for the latest results.
F
: Failed because the selector is not supported or the result was incorrect.
Selector | jsdom v24.1.1 (nwsapi) | happy-dom | linkeDom | patched-jsdom (dom-selector) | Result |
---|---|---|---|---|---|
simple selector:matches('.content') | 1,031,169 ops/sec ±0.30% | 7,650 ops/sec ±0.75% | 9,438 ops/sec ±0.58% | 828,526 ops/sec ±0.28% | jsdom is the fastest and 1.2 times faster than patched-jsdom. |
compound selector:matches('p.content[id]:is(:last-child, :only-child)') | 603,664 ops/sec ±1.24% | 7,430 ops/sec ±0.65% | 8,923 ops/sec ±0.98% | 400,799 ops/sec ±0.31% | jsdom is the fastest and 1.5 times faster than patched-jsdom. |
compound selector:matches('p.content[id]:is(:invalid-nth-child, :only-child)') | F | 7,358 ops/sec ±1.45% | F | 73,167 ops/sec ±0.99% | patched-jsdom is the fastest. |
compound selector:matches('p.content[id]:not(:is(.foo, .bar))') | 478,859 ops/sec ±1.56% | 7,312 ops/sec ±1.24% | 8,786 ops/sec ±0.75% | 338,833 ops/sec ±1.37% | jsdom is the fastest and 1.4 times faster than patched-jsdom. |
complex selector:matches('.box:first-child ~ .box:nth-of-type(4n+1) + .box[id] .block.inner > .content') | 154,200 ops/sec ±1.15% | F | 5,852 ops/sec ±0.74% | 127,543 ops/sec ±1.23% | jsdom is the fastest and 1.2 times faster than patched-jsdom. |
complex selector:matches('.box:first-child ~ .box:nth-of-type(4n+1) + .box[id] .block.inner:has(> .content)') | F | F | 5,792 ops/sec ±0.81% | 18,981 ops/sec ±1.80% | patched-jsdom is the fastest. |
complex selector within logical pseudo-class:matches(':is(.box > .content, .block > .content)') | 414,939 ops/sec ±1.14% | F | 6,158 ops/sec ±0.44% | 332,506 ops/sec ±0.26% | jsdom is the fastest and 1.2 times faster than patched-jsdom. |
Selector | jsdom v24.1.1 (nwsapi) | happy-dom | linkeDom | patched-jsdom (dom-selector) | Result |
---|---|---|---|---|---|
simple selector:closest('.container') | 376,552 ops/sec ±1.42% | 7,476 ops/sec ±0.84% | 9,281 ops/sec ±0.63% | 340,692 ops/sec ±1.37% | jsdom is the fastest and 1.1 times faster than patched-jsdom. |
compound selector:closest('div.container[id]:not(.foo, .box)') | 138,076 ops/sec ±1.49% | F | 8,643 ops/sec ±0.96% | 121,489 ops/sec ±1.12% | jsdom is the fastest and 1.1 times faster than patched-jsdom. |
complex selector:closest('.box:first-child ~ .box:nth-of-type(4n+1) + .box[id] .block.inner > .content') | 141,376 ops/sec ±1.33% | F | 5,795 ops/sec ±0.80% | 120,433 ops/sec ±1.28% | jsdom is the fastest and 1.2 times faster than patched-jsdom. |
complex selector:closest('.box:first-child ~ .box:nth-of-type(4n+1) + .box[id] .block.inner:has(> .content)') | F | F | 5,723 ops/sec ±0.73% | 15,392 ops/sec ±1.58% | patched-jsdom is the fastest. |
complex selector within logical pseudo-class:closest(':is(.container > .content, .container > .box)') | 194,922 ops/sec ±1.40% | 4,752 ops/sec ±1.20% | 6,139 ops/sec ±0.35% | 176,281 ops/sec ±0.32% | jsdom is the fastest and 1.1 times faster than patched-jsdom. |
Selector | jsdom v24.1.1 (nwsapi) | happy-dom | linkeDom | patched-jsdom (dom-selector) | Result |
---|---|---|---|---|---|
simple selector:querySelector('.content') | 27,103 ops/sec ±0.78% | 9,293 ops/sec ±0.55% | 11,341 ops/sec ±0.46% | 30,262 ops/sec ±0.23% | patched-jsdom is the fastest. patched-jsdom is 1.1 times faster than jsdom. |
compound selector:querySelector('p.content[id]:is(:last-child, :only-child)') | 10,072 ops/sec ±1.46% | 9,055 ops/sec ±0.95% | 10,058 ops/sec ±0.84% | 9,716 ops/sec ±1.34% | jsdom is the fastest and 1.0 times faster than patched-jsdom. |
complex selector:querySelector('.box:first-child ~ .box:nth-of-type(4n+1) + .box[id] .block.inner > .content') | 225 ops/sec ±0.61% | F | 1,267 ops/sec ±1.48% | 271 ops/sec ±1.37% | linkedom is the fastest and 4.7 times faster than patched-jsdom. patched-jsdom is 1.2 times faster than jsdom. |
complex selector:querySelector('.box:first-child ~ .box:nth-of-type(4n+1) + .box[id] .block.inner:has(> .content)') | F | F | 1,611 ops/sec ±1.52% | 730 ops/sec ±1.58% | linkedom is the fastest and 2.2 times faster than patched-jsdom. |
complex selector within logical pseudo-class:querySelector(':is(.box > .content, .block > .content)') | 3,152 ops/sec ±2.67% | F | 9,906 ops/sec ±0.94% | 159,199 ops/sec ±1.91% | patched-jsdom is the fastest. patched-jsdom is 50.5 times faster than jsdom. |
Selector | jsdom v24.1.1 (nwsapi) | happy-dom | linkeDom | patched-jsdom (dom-selector) | Result |
---|---|---|---|---|---|
simple selector:querySelectorAll('.content') | 2,684 ops/sec ±1.00% | 758 ops/sec ±1.65% | 1,231 ops/sec ±1.45% | 3,131 ops/sec ±0.90% | patched-jsdom is the fastest. patched-jsdom is 1.2 times faster than jsdom. |
compound selector:querySelectorAll('p.content[id]:is(:last-child, :only-child)') | 958 ops/sec ±0.29% | 686 ops/sec ±1.79% | 1,171 ops/sec ±1.23% | 996 ops/sec ±1.17% | linkedom is the fastest and 1.2 times faster than patched-jsdom. patched-jsdom is 1.0 times faster than jsdom. |
complex selector:querySelectorAll('.box:first-child ~ .box:nth-of-type(4n+1) + .box[id] .block.inner > .content') | 210 ops/sec ±1.78% | F | 421 ops/sec ±1.53% | 240 ops/sec ±1.51% | linkedom is the fastest and 1.8 times faster than patched-jsdom. patched-jsdom is 1.1 times faster than jsdom. |
complex selector:querySelectorAll('.box:first-child ~ .box:nth-of-type(4n+1) + .box[id] .block.inner:has(> .content)') | F | F | 442 ops/sec ±1.84% | 823 ops/sec ±1.64% | patched-jsdom is the fastest. |
complex selector within logical pseudo-class:querySelectorAll(':is(.box > .content, .block > .content)') | 302 ops/sec ±1.43% | F | 518 ops/sec ±1.47% | 785 ops/sec ±0.98% | patched-jsdom is the fastest. patched-jsdom is 2.6 times faster than jsdom. |
The following resources have been of great help in the development of the DOM Selector.
Copyright (c) 2023 asamuzaK (Kazz)
FAQs
A CSS selector engine.
We found that @asamuzakjp/dom-selector demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.
Research
Security News
A threat actor's playbook for exploiting the npm ecosystem was exposed on the dark web, detailing how to build a blockchain-powered botnet.