Security News
38% of CISOs Fear They’re Not Moving Fast Enough on AI
CISOs are racing to adopt AI for cybersecurity, but hurdles in budgets and governance may leave some falling behind in the fight against cyber threats.
@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
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);
};
}
});
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) | ✓ | |
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:popover-open | ✓ | |
E:state(v) | ✓ | *1 |
:host | ✓ | |
:host(s) | ✓ | |
:host‑context(s) | ✓ | |
:host(:state(v)) | ✓ | *1 |
:host:has(rs1, rs2, ...) | ✓ | |
:host(s):has(rs1, rs2, ...) | ✓ | |
:host‑context(s):has(rs1, rs2, ...) | ✓ | |
& | ✓ | Only supports outermost & , i.e. equivalent to :scope |
*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;
}
}
See benchmark for the latest results.
F
: Failed because the selector is not supported or the result was incorrect.
Selector | jsdom v26.0.0 (nwsapi) | happy-dom | linkeDom | patched-jsdom (dom-selector) | Result |
---|---|---|---|---|---|
simple selector:matches('.content') | 112,297 ops/sec ±4.41% | 403,800 ops/sec ±2.26% | 9,352 ops/sec ±1.18% | 109,587 ops/sec ±0.36% | happydom is the fastest and 3.7 times faster than patched-jsdom. jsdom is 1.0 times faster than patched-jsdom. |
compound selector:matches('p.content[id]:is(:last-child, :only-child)') | 102,156 ops/sec ±0.83% | 403,804 ops/sec ±0.91% | 9,051 ops/sec ±0.78% | 77,568 ops/sec ±4.06% | happydom is the fastest and 5.2 times faster than patched-jsdom. jsdom is 1.3 times faster than patched-jsdom. |
compound selector:matches('p.content[id]:is(:invalid-nth-child, :only-child)') | F | 387,515 ops/sec ±4.34% | F | 41,904 ops/sec ±1.57% | happydom is the fastest and 9.2 times faster than patched-jsdom. |
compound selector:matches('p.content[id]:not(:is(.foo, .bar))') | 97,333 ops/sec ±0.62% | 394,134 ops/sec ±2.82% | 8,821 ops/sec ±0.86% | 79,436 ops/sec ±0.69% | happydom is the fastest and 5.0 times faster than patched-jsdom. jsdom is 1.2 times faster than patched-jsdom. |
complex selector:matches('.box:first-child ~ .box:nth-of-type(4n+1) + .box[id] .block.inner > .content') | 63,065 ops/sec ±0.95% | F | 5,751 ops/sec ±0.73% | 57,917 ops/sec ±1.27% | jsdom is the fastest and 1.1 times faster than patched-jsdom. |
complex selector:matches('.box:first-child ~ .box:nth-of-type(4n+1) + .box[id] .block.inner:has(> .content)') | 29,609 ops/sec ±1.68% | F | 5,741 ops/sec ±1.12% | 39,522 ops/sec ±3.55% | patched-jsdom is the fastest. patched-jsdom is 1.3 times faster than jsdom. |
complex selector within logical pseudo-class:matches(':is(.box > .content, .block > .content)') | 89,634 ops/sec ±0.66% | F | 6,049 ops/sec ±0.33% | 83,881 ops/sec ±0.70% | jsdom is the fastest and 1.1 times faster than patched-jsdom. |
nested and chained :not() selector:matches('p:not(:is(:not(.content))):not(.foo)') | F | 377,155 ops/sec ±1.07% | 6,087 ops/sec ±0.70% | 60,032 ops/sec ±26.27% | happydom is the fastest and 6.3 times faster than patched-jsdom. |
Selector | jsdom v26.0.0 (nwsapi) | happy-dom | linkeDom | patched-jsdom (dom-selector) | Result |
---|---|---|---|---|---|
simple selector:closest('.container') | 68,204 ops/sec ±1.82% | 266,567 ops/sec ±40.99% | 9,268 ops/sec ±0.81% | 82,517 ops/sec ±0.79% | happydom is the fastest and 3.2 times faster than patched-jsdom. patched-jsdom is 1.2 times faster than jsdom. |
compound selector:closest('div.container[id]:not(.foo, .box)') | 60,091 ops/sec ±0.56% | F | 8,418 ops/sec ±1.06% | 51,243 ops/sec ±0.74% | 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 > .content') | 59,694 ops/sec ±0.61% | F | 5,801 ops/sec ±0.69% | 53,569 ops/sec ±0.65% | 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:has(> .content)') | 22,696 ops/sec ±0.97% | F | 5,800 ops/sec ±0.52% | 34,907 ops/sec ±1.00% | patched-jsdom is the fastest. patched-jsdom is 1.5 times faster than jsdom. |
complex selector within logical pseudo-class:closest(':is(.container > .content, .container > .box)') | 68,572 ops/sec ±0.55% | 338,382 ops/sec ±1.46% | 5,985 ops/sec ±1.24% | 64,454 ops/sec ±0.49% | happydom is the fastest and 5.2 times faster than patched-jsdom. jsdom is 1.1 times faster than patched-jsdom. |
nested and chained :not() selector:closest('div:not(:is(:not(.container))):not(.box)') | F | F | 8,638 ops/sec ±0.52% | 55,838 ops/sec ±34.28% | patched-jsdom is the fastest. |
Selector | jsdom v26.0.0 (nwsapi) | happy-dom | linkeDom | patched-jsdom (dom-selector) | Result |
---|---|---|---|---|---|
simple selector:querySelector('.content') | 15,912 ops/sec ±2.11% | 307,120 ops/sec ±26.93% | 10,595 ops/sec ±0.99% | 71,245 ops/sec ±1.77% | happydom is the fastest and 4.3 times faster than patched-jsdom. patched-jsdom is 4.5 times faster than jsdom. |
compound selector:querySelector('p.content[id]:is(:last-child, :only-child)') | 7,333 ops/sec ±0.86% | 290,078 ops/sec ±42.38% | 9,955 ops/sec ±1.82% | 34,346 ops/sec ±1.15% | happydom is the fastest and 8.4 times faster than patched-jsdom. patched-jsdom is 4.7 times faster than jsdom. |
complex selector:querySelector('.box:first-child ~ .box:nth-of-type(4n+1) + .box[id] .block.inner > .content') | 123 ops/sec ±1.40% | F | 1,021 ops/sec ±0.53% | 553 ops/sec ±1.99% | linkedom is the fastest and 1.8 times faster than patched-jsdom. patched-jsdom is 4.5 times faster than jsdom. |
complex selector:querySelector('.box:first-child ~ .box:nth-of-type(4n+1) + .box[id] .block.inner:has(> .content)') | 40.29 ops/sec ±6.51% | F | 1,304 ops/sec ±0.87% | 382 ops/sec ±1.12% | linkedom is the fastest and 3.4 times faster than patched-jsdom. patched-jsdom is 9.5 times faster than jsdom. |
complex selector within logical pseudo-class:querySelector(':is(.box > .content, .block > .content)') | 2,104 ops/sec ±0.69% | F | 9,762 ops/sec ±0.68% | 67,358 ops/sec ±0.69% | patched-jsdom is the fastest. patched-jsdom is 32.0 times faster than jsdom. |
nested and chained :not() selector:querySelector('p:not(:is(:not(.content))):not(.foo)') | F | 354,191 ops/sec ±1.43% | 9,950 ops/sec ±0.86% | 65,426 ops/sec ±0.91% | happydom is the fastest and 5.4 times faster than patched-jsdom. |
Selector | jsdom v26.0.0 (nwsapi) | happy-dom | linkeDom | patched-jsdom (dom-selector) | Result |
---|---|---|---|---|---|
simple selector:querySelectorAll('.content') | 938 ops/sec ±1.61% | 285 ops/sec ±3.27% | 993 ops/sec ±2.52% | 794 ops/sec ±37.70% | linkedom is the fastest and 1.3 times faster than patched-jsdom. jsdom is 1.2 times faster than patched-jsdom. |
compound selector:querySelectorAll('p.content[id]:is(:last-child, :only-child)') | 331 ops/sec ±2.79% | 320 ops/sec ±4.68% | 949 ops/sec ±0.89% | 354 ops/sec ±26.10% | linkedom is the fastest and 2.7 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 > .content') | 125 ops/sec ±0.67% | F | 298 ops/sec ±0.85% | 140 ops/sec ±1.03% | linkedom is the fastest and 2.1 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)') | 42.34 ops/sec ±0.89% | F | 351 ops/sec ±0.89% | 135 ops/sec ±1.36% | linkedom is the fastest and 2.6 times faster than patched-jsdom. patched-jsdom is 3.2 times faster than jsdom. |
complex selector within logical pseudo-class:querySelectorAll(':is(.box > .content, .block > .content)') | 161 ops/sec ±1.07% | F | 369 ops/sec ±1.11% | 720 ops/sec ±1.43% | patched-jsdom is the fastest. patched-jsdom is 4.5 times faster than jsdom. |
nested and chained :not() selector:querySelectorAll('p:not(:is(:not(.content))):not(.foo)') | F | 296 ops/sec ±46.31% | 998 ops/sec ±0.79% | 1,168 ops/sec ±1.12% | patched-jsdom is the fastest. |
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.
The npm package @asamuzakjp/dom-selector receives a total of 171,432 weekly downloads. As such, @asamuzakjp/dom-selector popularity was classified as popular.
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
CISOs are racing to adopt AI for cybersecurity, but hurdles in budgets and governance may leave some falling behind in the fight against cyber threats.
Research
Security News
Socket researchers uncovered a backdoored typosquat of BoltDB in the Go ecosystem, exploiting Go Module Proxy caching to persist undetected for years.
Security News
Company News
Socket is joining TC54 to help develop standards for software supply chain security, contributing to the evolution of SBOMs, CycloneDX, and Package URL specifications.