@@ -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