4.2.2 • Published 2 days ago

@asamuzakjp/dom-selector v4.2.2

Weekly downloads
-
License
MIT
Repository
github
Last release
2 days ago

DOM Selector

build CodeQL npm (scoped)

A CSS selector engine.

Install

npm i @asamuzakjp/dom-selector

Usage

import { DOMSelector } from '@asamuzakjp/dom-selector';
import { JSDOM } from 'jsdom';

const { window } = new JSDOM();
const {
  closest, matches, querySelector, querySelectorAll
} = new DOMSelector(window);

matches(selector, node, opt)

matches - same functionality as Element.matches()

Parameters

  • selector string CSS selector
  • node object Element node
  • opt object? options
    • opt.noexcept boolean? no exception
    • opt.warn boolean? console warn e.g. unsupported pseudo-class

Returns boolean true if matched, false otherwise

closest(selector, node, opt)

closest - same functionality as Element.closest()

Parameters

  • selector string CSS selector
  • node object Element node
  • opt object? options
    • opt.noexcept boolean? no exception
    • opt.warn boolean? console warn e.g. unsupported pseudo-class

Returns object? matched node

querySelector(selector, node, opt)

querySelector - same functionality as Document.querySelector(), DocumentFragment.querySelector(), Element.querySelector()

Parameters

  • selector string CSS selector
  • node object Document, DocumentFragment or Element node
  • opt object? options
    • opt.noexcept boolean? no exception
    • opt.warn boolean? console warn e.g. unsupported pseudo-class

Returns object? matched node

querySelectorAll(selector, node, opt)

querySelectorAll - same functionality as Document.querySelectorAll(), DocumentFragment.querySelectorAll(), Element.querySelectorAll()
NOTE: returns Array, not NodeList

Parameters

  • selector string CSS selector
  • node object Document, DocumentFragment or Element node
  • opt object? options
    • opt.noexcept boolean? no exception
    • opt.warn boolean? console warn e.g. unsupported pseudo-class

Returns Array<(object | undefined)> array of matched nodes

Supported CSS selectors

PatternSupportedNote
*
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:definedPartially supportedMatching with MathML is not yet supported.
E:dir(ltr)
E:lang(en)Partially supportedComma-separated list of language codes, e.g. :lang(en, fr), is not yet supported.
E:anylink
E:link
E:visitedReturns false or null to prevent fingerprinting.
E:locallink
E:target
E:targetwithin
E:scope
E:currentUnsupported
E:current(s)Unsupported
E:pastUnsupported
E:futureUnsupported
E:activeUnsupported
E:hoverUnsupported
E:focus
E:focuswithin
E:focusvisibleUnsupported
E:openE:closedPartially supportedMatching with <select>, e.g. select:open, is not supported.
E:enabledE:disabled
E:readwriteE:readonly
E:placeholdershown
E:default
E:checked
E:indeterminate
E:validE:invalid
E:requiredE:optional
E:blankUnsupported
E:uservalidE:userinvalidUnsupported
E:root
E:empty
E:nthchild(n of S?)
E:nthlastchild(n of S?)
E:firstchild
E:lastchild
E:onlychild
E:nthoftype(n)
E:nthlastoftype(n)
E:firstoftype
E:lastoftype
E:onlyoftype
E F
E > F
E + F
E ~ F
F || EUnsupported
E:nthcol(n)Unsupported
E:nthlastcol(n)Unsupported
E:host
E:host(s)
E:hostcontext(s)

Monkey patch jsdom

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);
    };
  }
});

Performance

See benchmark for the latest results.

F: Failed because the selector is not supported or the result is incorrect.

matches()

Selectorjsdom v24.0.0 (nwsapi)happy-domlinkeDompatched-jsdom (dom-selector)Result
simple selector:matches('.content')993,434 ops/sec ±0.92%7,738 ops/sec ±0.82%9,388 ops/sec ±0.63%934,402 ops/sec ±1.24%jsdom is the fastest and 1.1 times faster than patched-jsdom.
compound selector:matches('p.content[id]:is(:last-child, :only-child)')593,746 ops/sec ±0.24%7,510 ops/sec ±0.73%9,048 ops/sec ±1.11%510,470 ops/sec ±0.17%jsdom is the fastest and 1.2 times faster than patched-jsdom.
compound selector:matches('p.content[id]:is(:invalid-nth-child, :only-child)')F7,532 ops/sec ±0.64%F148,751 ops/sec ±1.43%patched-jsdom is the fastest.
compound selector:matches('p.content[id]:not(:is(.foo, .bar))')478,481 ops/sec ±0.32%7,437 ops/sec ±0.57%8,922 ops/sec ±0.47%406,300 ops/sec ±0.32%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 > .content')151,608 ops/sec ±0.24%F5,749 ops/sec ±0.57%136,036 ops/sec ±0.46%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 .block.inner:has(> .content)')FF5,654 ops/sec ±0.57%40,774 ops/sec ±0.59%patched-jsdom is the fastest.
complex selector within logical pseudo-class:matches(':is(.box > .content, .block > .content)')FF6,243 ops/sec ±0.45%146,879 ops/sec ±1.92%patched-jsdom is the fastest.

closest()

Selectorjsdom v24.0.0 (nwsapi)happy-domlinkeDompatched-jsdom (dom-selector)Result
simple selector:closest('.container')362,505 ops/sec ±0.41%7,607 ops/sec ±0.62%9,365 ops/sec ±0.59%362,275 ops/sec ±0.39%jsdom is the fastest and 1.0 times faster than patched-jsdom.
compound selector:closest('div.container[id]:not(.foo, .box)')137,678 ops/sec ±0.32%F8,749 ops/sec ±0.67%129,174 ops/sec ±0.30%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')139,504 ops/sec ±0.43%F5,774 ops/sec ±0.51%126,287 ops/sec ±0.32%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 .block.inner:has(> .content)')FF5,710 ops/sec ±0.36%29,552 ops/sec ±0.90%patched-jsdom is the fastest.
complex selector within logical pseudo-class:closest(':is(.container > .content, .container > .box)')F4,754 ops/sec ±0.73%5,935 ops/sec ±0.32%64,421 ops/sec ±0.98%patched-jsdom is the fastest.

querySelector()

Selectorjsdom v24.0.0 (nwsapi)happy-domlinkeDompatched-jsdom (dom-selector)Result
simple selector:querySelector('.content')28,406 ops/sec ±0.62%9,286 ops/sec ±0.63%10,750 ops/sec ±0.59%25,303 ops/sec ±1.00%jsdom is the fastest and 1.1 times faster than patched-jsdom.
compound selector:querySelector('p.content[id]:is(:last-child, :only-child)')9,564 ops/sec ±0.33%8,884 ops/sec ±0.59%10,834 ops/sec ±0.60%8,809 ops/sec ±0.53%linkedom is the fastest and 1.2 times faster than patched-jsdom. jsdom is 1.1 times faster than patched-jsdom.
complex selector:querySelector('.box:first-child ~ .box:nth-of-type(4n+1) + .box[id] .block.inner > .content')228 ops/sec ±0.41%F1,390 ops/sec ±0.61%713 ops/sec ±2.32%linkedom is the fastest and 1.9 times faster than patched-jsdom. patched-jsdom is 3.1 times faster than jsdom.
complex selector:querySelector('.box:first-child ~ .box:nth-of-type(4n+1) + .box .block.inner:has(> .content)')FF1,523 ops/sec ±1.72%494 ops/sec ±1.97%linkedom is the fastest and 3.1 times faster than patched-jsdom.
complex selector within logical pseudo-class:querySelector(':is(.box > .content, .block > .content)')FF9,987 ops/sec ±0.59%101,461 ops/sec ±1.56%patched-jsdom is the fastest.

querySelectorAll()

Selectorjsdom v24.0.0 (nwsapi)happy-domlinkeDompatched-jsdom (dom-selector)Result
simple selector:querySelectorAll('.content')2,713 ops/sec ±1.04%729 ops/sec ±0.29%1,187 ops/sec ±0.40%2,988 ops/sec ±0.19%patched-jsdom is the fastest. patched-jsdom is 1.1 times faster than jsdom.
compound selector:querySelectorAll('p.content[id]:is(:last-child, :only-child)')946 ops/sec ±0.26%724 ops/sec ±0.35%1,194 ops/sec ±0.25%971 ops/sec ±0.25%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')225 ops/sec ±1.41%F416 ops/sec ±1.46%818 ops/sec ±1.67%patched-jsdom is the fastest. patched-jsdom is 3.6 times faster than jsdom.
complex selector:querySelectorAll('.box:first-child ~ .box:nth-of-type(4n+1) + .box .block.inner:has(> .content)')FF443 ops/sec ±1.47%530 ops/sec ±1.87%patched-jsdom is the fastest.
complex selector within logical pseudo-class:querySelectorAll(':is(.box > .content, .block > .content)')FF525 ops/sec ±0.27%255 ops/sec ±1.13%linkedom is the fastest and 2.1 times faster than patched-jsdom.

Acknowledgments

The following resources have been of great help in the development of the DOM Selector.


Copyright (c) 2023 asamuzaK (Kazz)

4.2.2

2 days ago

4.2.1

6 days ago

4.2.0

1 month ago

4.1.7

2 months ago

4.1.4

3 months ago

4.1.6

3 months ago

4.1.5

3 months ago

4.1.3

3 months ago

4.1.2

3 months ago

4.1.1

3 months ago

4.1.0

3 months ago

4.0.1

3 months ago

4.0.0

3 months ago

3.0.5

3 months ago

3.0.4

3 months ago

3.0.3

3 months ago

3.0.2

3 months ago

2.1.0-b.3

4 months ago

2.1.0-b.2

4 months ago

2.1.0-b.4

4 months ago

3.0.1

4 months ago

2.1.0-b.1

4 months ago

2.0.3-a.6

4 months ago

2.0.3-a.5

4 months ago

2.0.3-a.8

4 months ago

2.0.3-a.7

4 months ago

2.0.3-a.4

4 months ago

2.0.3-a.2

4 months ago

2.0.3-a.3

4 months ago

2.0.2

4 months ago

2.0.2-a.3

4 months ago

2.0.3-a.1

4 months ago

2.0.2-a.1

4 months ago

2.0.2-a.2

4 months ago

2.0.1

4 months ago

2.0.0

4 months ago

1.2.8

4 months ago

1.2.7

4 months ago

1.2.6

4 months ago

1.2.5

4 months ago

1.2.4

4 months ago

1.2.3

4 months ago

1.2.2

4 months ago

1.2.1

4 months ago

1.2.0

4 months ago

1.1.14

5 months ago

1.1.13

5 months ago

1.1.9

5 months ago

1.1.8

5 months ago

1.1.6

5 months ago

1.1.5

6 months ago

1.1.4

6 months ago

1.1.3

6 months ago

1.1.12

5 months ago

1.1.11

5 months ago

1.1.10

5 months ago

1.0.2

6 months ago

1.0.1

6 months ago

1.0.0

6 months ago

1.0.5

6 months ago

1.0.4

6 months ago

1.0.3

6 months ago

1.1.1

6 months ago

1.1.0

6 months ago

1.1.2

6 months ago

0.24.0

6 months ago

0.23.2

6 months ago

0.21.2

7 months ago

0.21.1

7 months ago

0.21.0

7 months ago

0.22.0

6 months ago

0.20.1

7 months ago

0.20.0

7 months ago

0.19.4

10 months ago

0.19.5

10 months ago

0.19.6

8 months ago

0.20.2

7 months ago

0.15.4

12 months ago

0.19.1

11 months ago

0.15.5

12 months ago

0.19.2

11 months ago

0.15.6

12 months ago

0.19.3

11 months ago

0.15.7

12 months ago

0.15.8

12 months ago

0.15.9

12 months ago

0.17.0

11 months ago

0.15.3

12 months ago

0.18.1

11 months ago

0.16.3

11 months ago

0.16.4

11 months ago

0.16.0

11 months ago

0.16.1

11 months ago

0.18.0

11 months ago

0.16.2

11 months ago

0.15.10

12 months ago

0.15.13

11 months ago

0.15.12

11 months ago

0.15.1

12 months ago

0.14.0

12 months ago

0.13.5

12 months ago

0.13.2

12 months ago

0.13.1

12 months ago

0.12.8

12 months ago

0.12.7

12 months ago

0.12.6

12 months ago

0.12.5

12 months ago

0.12.3

12 months ago

0.12.2

12 months ago

0.12.1

12 months ago

0.12.0

12 months ago

0.11.1

12 months ago

0.10.0

12 months ago

0.9.2

12 months ago

0.8.1

12 months ago

0.8.0

12 months ago

0.7.0

12 months ago

0.6.2

12 months ago

0.6.1

12 months ago

0.6.0

12 months ago

0.5.0

12 months ago

0.4.2

1 year ago

0.4.1

1 year ago

0.3.0

1 year ago

0.2.2

1 year ago

0.2.1

1 year ago