@@ -4,6 +4,8 @@ import getNodeAttributes from './get-node-attributes';
44import matchesSelector from './element-matches' ;
55import isXHTML from './is-xhtml' ;
66import getShadowSelector from './get-shadow-selector' ;
7+ import memoize from './memoize' ;
8+ import constants from '../../core/constants' ;
79
810const ignoredAttributes = [
911 'class' ,
@@ -376,8 +378,8 @@ function generateSelector(elm, options, doc) {
376378 } else {
377379 selector = features ;
378380 }
379- if ( ! similar ) {
380- similar = Array . from ( doc . querySelectorAll ( selector ) ) ;
381+ if ( ! similar || similar . length > constants . selectorSimilarFilterLimit ) {
382+ similar = findSimilar ( doc , selector ) ;
381383 } else {
382384 similar = similar . filter ( item => {
383385 return matchesSelector ( item , selector ) ;
@@ -401,6 +403,16 @@ function generateSelector(elm, options, doc) {
401403 * @param {Object } optional options
402404 * @returns {String|Array<String> } Unique CSS selector for the node
403405 */
404- export default function getSelector ( elm , options ) {
406+ function getSelector ( elm , options ) {
405407 return getShadowSelector ( generateSelector , elm , options ) ;
406408}
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