Hacktoberfest 2026: as issues que os mantenedores marcaram para outubro, abertas e boas para iniciantes. Ver issues do Hacktoberfest

Element#matches() compiles the selector on every call

Aberta
#348 0 comentários 0 reações 0 responsáveis Ver no GitHub

Mantenedores costumam responder em até 17 dias

@jonasporto já está trabalhando nisso.

Desde 16/10/2026.

  • #349 de @jonasporto — aberto

Avaliação

Dificuldade
2/5
Tempo estimado
1-3 horas
Facilidade para iniciantes
35/100
Tipo de issue
Refatoração
Clareza
Claramente especificada
Status de atividade
Estagnada
Stack de tecnologia
css, javascript
Domínio
backend, performance

Direção de pesquisa

The hot path is esm/shared/matches.js near line 109, where Element#matches() passes the selector string to CSSselect.is(), which recompiles it on every call; prepareMatch already compiles once per call for querySelector* and closest(), so look there for the pattern to reuse. Pull request #349 is already open against this, so check it before starting. Done means a compiled selector is cached per selector string and document kind in a small bounded Map, :scope selectors still compile per call, and the existing matches tests pass with coverage unchanged.

Escrita pelo modelo de indexação a partir do texto da issue.

Descrição

Element#matches(selectors) parses and compiles the selector again on every call. Code that tests many elements against the same few selectors (resolving a <style> sheet over an SVG chart, for example) spends most of its time in css-what's parser and css-select's compiler rather than in matching.

// npm i linkedom && node matches.mjs
import { parseHTML } from 'linkedom';

let svg = '<svg id="chart">';
for (let i = 0; i < 400; i++)
  svg += `<g class="node n${i % 7}"><rect class="box"></rect><g class="label"><text class="title"><tspan>${i}</tspan></text></g></g>`;
const { document } = parseHTML(`<!doctype html><html><body>${svg}</svg></body></html>`);

const selectors = [];
for (const a of ['#chart', '#chart .node', '#chart g.label', 'svg'])
  for (const b of ['rect', 'text', 'tspan', '.label', '.title', '.box', '.n3', 'g', '.cluster rect', '.edgeLabel', 'g.node', '.missing'])
    selectors.push(`${a} ${b}`);

const elements = [...document.querySelectorAll('svg, svg *')];
const t = performance.now();
for (const el of elements)
  for (const sel of selectors)
    el.matches(sel);
console.log(`${elements.length * selectors.length} calls: ${(performance.now() - t).toFixed(0)} ms`);

Node 22.17.0, macOS, median of runs:

96,048 matches() calls
linkedom 0.18.13 ~112 ms
linkedom main (3fcf145) ~108 ms
same, with the compiled selector kept per selector string ~20 ms

A larger run (2,001 elements × 50 selectors, 300k calls), in ms per 100k calls: linkedom 0.18.13 158, main 111, with the compiled selector kept 17; happy-dom 21.0.3 10, jsdom 30.1.3 209.

A CPU profile of main shows css-what parseSelector/unescapeCSS, css-select compileToken/escapeRegex and GC on top; the matching itself is a small share.

Where: esm/shared/matches.js#L109 passes the selector string to CSSselect.is(), which calls compile(query, options) each time (css-select 7.0.0, dist/index.js, is). querySelector* and closest() compile once per call through prepareMatch, so it is matches() that pays per element.

Possible fix: keep the compiled function per selector string and document kind (xmlMode), in a small bounded Map; selectors with :scope keep compiling per call since their context is the element. I have this with a test (coverage unchanged) and can open a PR.

Linguagem predominante
HTML
Estrelas
2.1k
Forks
110
Merge médio
11d 1h
PRs com merge (30d)
1

Preparar o ambiente

Este projeto não oferece contêiner de desenvolvimento, Dockerfile nem guia de contribuição, então a configuração fica por sua conta: comece pelo README e veja nosso guia da primeira contribuição para os passos gerais.

Primeiros passos

  1. Leia a issue inteira e depois o guia de contribuição do projeto.
  2. Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
  3. Faça um fork do repositório e trabalhe em uma branch.
  4. Abra um pull request que referencie o número da issue.

Mais de WebReflection/linkedom

Todas as issues de WebReflection/linkedom

Issues semelhantes

Mais issues de Backend & API Design

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.