Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 24 additions & 9 deletions cjs/shared/matches.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,29 @@ const prepareMatch = (element, selectors) => CSSselect.compile(
);
exports.prepareMatch = prepareMatch;

const matches = (element, selectors) => CSSselect.is(
element,
selectors,
{
strict: true,
context: selectors.includes(':scope') ? element : void 0,
xmlMode: !ignoreCase(element),
adapter
// matches() is called once per element and selector pair, so the selector
// is compiled once and kept, instead of parsed again on every call.
// :scope depends on the element, so those are compiled each time.
const compiled = new Map;
const MAX_COMPILED = 1024;

/** @returns {boolean} */
const matches = (element, selectors) => {
const xmlMode = !ignoreCase(element);
if (selectors.includes(':scope'))
return CSSselect.is(
element,
selectors,
{strict: true, context: element, xmlMode, adapter}
);
const key = (xmlMode ? 'x' : 'h') + selectors;
let test = compiled.get(key);
if (!test) {
if (compiled.size === MAX_COMPILED)
compiled.delete(compiled.keys().next().value);
test = CSSselect.compile(selectors, {strict: true, xmlMode, adapter});
compiled.set(key, test);
}
);
return test(element);
};
exports.matches = matches;
33 changes: 24 additions & 9 deletions esm/shared/matches.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,28 @@ export const prepareMatch = (element, selectors) => CSSselect.compile(
}
);

export const matches = (element, selectors) => CSSselect.is(
element,
selectors,
{
strict: true,
context: selectors.includes(':scope') ? element : void 0,
xmlMode: !ignoreCase(element),
adapter
// matches() is called once per element and selector pair, so the selector
// is compiled once and kept, instead of parsed again on every call.
// :scope depends on the element, so those are compiled each time.
const compiled = new Map;
const MAX_COMPILED = 1024;

/** @returns {boolean} */
export const matches = (element, selectors) => {
const xmlMode = !ignoreCase(element);
if (selectors.includes(':scope'))
return CSSselect.is(
element,
selectors,
{strict: true, context: element, xmlMode, adapter}
);
const key = (xmlMode ? 'x' : 'h') + selectors;
let test = compiled.get(key);
if (!test) {
if (compiled.size === MAX_COMPILED)
compiled.delete(compiled.keys().next().value);
test = CSSselect.compile(selectors, {strict: true, xmlMode, adapter});
compiled.set(key, test);
}
);
return test(element);
};
12 changes: 12 additions & 0 deletions test/html/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,18 @@ emptyClass.getElementById('b').className = 'x';
emptyClass.getElementById('b').classList.remove('x');
assert(emptyClass.querySelectorAll('[class=""]').map(({id}) => id).join(','), 'b,c', '[class=""] follows class mutations');

const {document: compiled} = parseHTML('<div><p class="a"></p></div>');
const compiledP = compiled.querySelector('p');
assert(compiledP.matches('div .a'), true, 'matches() compiles a selector');
compiledP.className = 'b';
assert(compiledP.matches('div .a'), false, 'a compiled selector follows mutations');
const compiledXML = (new DOMParser).parseFromString('<div><p class="a"/></div>', 'text/xml');
assert(compiledXML.querySelector('p').matches('DIV P'), false, 'compiled selectors are kept per document kind');
assert(compiledP.matches('DIV P'), true, 'compiled selectors are kept per document kind');
for (let i = 0; i < 1100; i++)
compiledP.matches(`.b, .c${i}`);
assert(compiledP.matches('div .b'), true, 'the oldest compiled selectors are dropped');

assert((new DOMParser).parseFromString('...', 'text/html').firstElementChild.localName, 'html');

const issue187 = (new DOMParser).parseFromString(`<!DOCTYPE html><html><head></head><body></body></html>`, 'text/html').defaultView.window.document;
Expand Down