You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
LAPTOP-S9HJSOEB\昊天 8001cd7178 第一次上传 11 months ago
..
dist/cjs 第一次上传 11 months ago
node_modules/lru-cache 第一次上传 11 months ago
src 第一次上传 11 months ago
types 第一次上传 11 months ago
LICENSE 第一次上传 11 months ago
README.md 第一次上传 11 months ago
package.json 第一次上传 11 months ago

README.md

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 - equivalent to 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 - equivalent to 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 - equivalent to Document.querySelector(), DocumentFragment.querySelector() and 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 - equivalent to Document.querySelectorAll(), DocumentFragment.querySelectorAll() and 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

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

Supported CSS selectors

Pattern Supported Note
* ✓
E ✓
ns|E ✓
*|E ✓
|E ✓
E F ✓
E > F ✓
E + F ✓
E ~ F ✓
F || E Unsupported
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:is(s1, s2, …) ✓
E:not(s1, s2, …) ✓
E:where(s1, s2, …) ✓
E:has(rs1, rs2, …) ✓
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:hover ✓
E:active ✓
E:focus ✓
E:focus‑visible ✓
E:focus‑within ✓
E:current Unsupported
E:current(s) Unsupported
E:past Unsupported
E:future Unsupported
E:open
E:closed
Partially supported Matching with <select>, e.g. select:open, is not supported.
E:popover-open ✓
E:enabled
E:disabled
✓
E:read‑write
E:read‑only
✓
E:placeholder‑shown ✓
E:default ✓
E:checked ✓
E:indeterminate ✓
E:blank Unsupported
E:valid
E:invalid
✓
E:in-range
E:out-of-range
✓
E:required
E:optional
✓
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:nth‑col(n) Unsupported
E:nth‑last‑col(n) Unsupported
CE:state(v) ✓ *1
:host ✓
:host(s) ✓
:host(:state(v)) ✓ *1
:host:has(rs1, rs2, ...) ✓
:host(s):has(rs1, rs2, ...) ✓
:host‑context(s) ✓
: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;
  }
}

Performance

See benchmark for the latest results.

Acknowledgments

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


Copyright (c) 2023 asamuzaK (Kazz)