@@ -4,6 +4,8 @@ import getNodeAttributes from './get-node-attributes';
4
4
import matchesSelector from './element-matches' ;
5
5
import isXHTML from './is-xhtml' ;
6
6
import getShadowSelector from './get-shadow-selector' ;
7
+ import memoize from './memoize' ;
8
+ import constants from '../../core/constants' ;
7
9
8
10
const ignoredAttributes = [
9
11
'class' ,
@@ -376,8 +378,8 @@ function generateSelector(elm, options, doc) {
376
378
} else {
377
379
selector = features ;
378
380
}
379
- if ( ! similar ) {
380
- similar = Array . from ( doc . querySelectorAll ( selector ) ) ;
381
+ if ( ! similar || similar . length > constants . selectorSimilarFilterLimit ) {
382
+ similar = findSimilar ( doc , selector ) ;
381
383
} else {
382
384
similar = similar . filter ( item => {
383
385
return matchesSelector ( item , selector ) ;
@@ -401,6 +403,16 @@ function generateSelector(elm, options, doc) {
401
403
* @param {Object } optional options
402
404
* @returns {String|Array<String> } Unique CSS selector for the node
403
405
*/
404
- export default function getSelector ( elm , options ) {
406
+ function getSelector ( elm , options ) {
405
407
return getShadowSelector ( generateSelector , elm , options ) ;
406
408
}
409
+
410
+ // Axe can call getSelector more than once for the same element because
411
+ // the same element can end up on multiple DqElements.
412
+ export default memoize ( getSelector ) ;
413
+
414
+ // Similar elements create similar selectors. If there are lots of similar elements on the page,
415
+ // axe ends up needing to run that same selector many times. We can memoize for a huge perf boost.
416
+ const findSimilar = memoize ( ( doc , selector ) =>
417
+ Array . from ( doc . querySelectorAll ( selector ) )
418
+ ) ;
0 commit comments