question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

querySelectorAll() with attribute selector which tests value is slow for URI-based values

See original GitHub issue

This is more specific issue instead of #677.

  1. Run this script to get a test page:
'use strict';

const fs = require('fs');

const html =  fs.openSync('test.html', 'w');

fs.writeSync(html,
  '\uFEFF<!doctype html><html><head><meta charset="UTF-8"><title></title></head><body>\n\n',
  null, 'utf8');

let i = 100000;
while (i--) {
  fs.writeSync(html, `<a title='${i}.html' href='${i}.html'></a>\n`, null, 'utf8');
}

fs.writeSync(html, '\n</body></html>\n', null, 'utf8');

You will get 100000 a elements with title and href attributes with identical values.

  1. Run these four scripts one by one. Do not combine all the four tests in one script because of memoization in the querySelectorAll() which affects performance of consecutive calls. Compare the outputs.
require('jsdom').env({file: 'test.html', done: (err, window) => {
  let hrStart;
  let hrEnd;

  hrStart = process.hrtime();
  console.log(window.document.querySelectorAll('a[title]').length);
  hrEnd = process.hrtime(hrStart);
console.log(`${(hrEnd[0] * 1e9 + hrEnd[1]) / 1e9} s\n`);
}});
100000
0.318559525 s
require('jsdom').env({file: 'test.html', done: (err, window) => {
  let hrStart;
  let hrEnd;

  hrStart = process.hrtime();
  console.log(window.document.querySelectorAll('a[title*=".html"]').length);
  hrEnd = process.hrtime(hrStart);
  console.log(`${(hrEnd[0] * 1e9 + hrEnd[1]) / 1e9} s\n`);
}});
100000
0.559967377 s
require('jsdom').env({file: 'test.html', done: (err, window) => {
  let hrStart;
  let hrEnd;

  hrStart = process.hrtime();
  console.log(window.document.querySelectorAll('a[href]').length);
  hrEnd = process.hrtime(hrStart);
  console.log(`${(hrEnd[0] * 1e9 + hrEnd[1]) / 1e9} s\n`);
}});
100000
0.34831147 s
require('jsdom').env({file: 'test.html', done: (err, window) => {
  let hrStart;
  let hrEnd;

  hrStart = process.hrtime();
  console.log(window.document.querySelectorAll('a[href*=".html"]').length);
  hrEnd = process.hrtime(hrStart);
  console.log(`${(hrEnd[0] * 1e9 + hrEnd[1]) / 1e9} s\n`);
}});
100000
9.444107183 s

As you can see, the cause of slowness is not the value testing by itself, but the testing of the href value.

  1. Run the second and the fourth scripts with profiling (see this guide). You can see this hot spot in the --prof-process output for the fourth script (it is missing in the output for second script):
 [Bottom up (heavy) profile]:
  Note: percentage shows a share of a particular caller in the total
  amount of its parent calls.
  Callers occupying less than 2.0% are not shown.

   ticks parent  name
  10422   57.9%  C:\Program Files\nodejs\node.exe
   8311   79.7%    C:\Program Files\nodejs\node.exe
   2868   34.5%      LazyCompile: *URLStateMachine ...\node_modules\whatwg-url\lib\url-state-machine.js:423:25
   2861   99.8%        LazyCompile: *exports.resolveURLToResultingParsedURL ...\node_modules\jsdom\lib\jsdom\living\helpers\document-base-url.js:51:42
   2861  100.0%          LazyCompile: setTheURL ...\node_modules\jsdom\lib\jsdom\living\nodes\HTMLHyperlinkElementUtils-impl.js:261:19
   2859   99.9%            LazyCompile: *getAttribute ...\node_modules\nwmatcher\src\nwmatcher-noqsa.js:321:13

In the getAttribute function of the nwmatcher-noqsa.js attributes are dealing with differently if they match the ATTR_URIDATA list. It seems to be the turning point. However I can’t trace all the parsing chain to URLStateMachine because I lack for understanding of all the jsdom complexity and components connections.

Is there any possible reason for this slowing-down additional parsing calls chain in the mere string match testing?

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:22 (21 by maintainers)

github_iconTop GitHub Comments

1reaction
domeniccommented, Oct 13, 2016

Yeah, that sounds good to me 😃. Thanks very much for testing!!

1reaction
dperinicommented, Oct 12, 2016

The issue should have been fixed by this commit:

https://github.com/dperini/nwmatcher/commit/1e30d83

please confirm and close.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Element.querySelectorAll() - Web APIs | MDN
The Element method querySelectorAll() returns a static (not live) NodeList ... which have a "data-active" attribute whose value is "1" :.
Read more >
Performance using JS querySelector [closed] - Stack Overflow
Firstly, document.querySelector("#elem");. Has an advantage in the fact that, unlike document.getElementId, it can return classes.
Read more >
How performant are data attributes as selectors
To run our tests, we'll cache the value of performance.now() , selector our items by class or data attribute, and then cache the...
Read more >
Don't use data attributes to find HTML elements with JS - intuio
querySelector (".cars");. As we can see, both CSS and JS use the same class to get their job done ...
Read more >
Javascript Tips & Tricks - ObservePoint Help Center
querySelector ('[{insert HTML Property here}="{insert value here}"]'); ... toLowerCase(), l = e.attributes, n = []; for (i = 0; i < l.length; ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found