{"action":"created","comment":{"url":"https://api.github.com/repos/dequelabs/axe-core/pulls/comments/170244614","pull_request_review_id":98903560,"id":170244614,"diff_hunk":"@@ -64,123 +192,103 @@ const createSelector = {\n       return id;\n     }\n   },\n-  // Get custom element name\n-  getCustomElm (elm, { isCustomElm, nodeName }) {\n-    if (isCustomElm) {\n-      return nodeName;\n-    }\n-  },\n-\n-  // Get ARIA role\n-  getElmRoleProp (elm) {\n-    if (elm.hasAttribute('role')) {\n-      return '[role=\"' + escapeSelector(elm.getAttribute('role')) +'\"]';\n-    }\n-  },\n-  // Get uncommon node names\n-  getUncommonElm (elm, { isCommonElm, isCustomElm, nodeName })  {\n-    if (!isCommonElm && !isCustomElm) {\n-      // Add [type] if nodeName is an input element\n-      if (nodeName === 'input' && elm.hasAttribute('type')) {\n-        nodeName += '[type=\"' + elm.type + '\"]';\n-      }\n-      return nodeName;\n-    }\n-  },\n-  // Has a name property, but no ID (Think input fields)\n-  getElmNameProp (elm) {\n-    if (!elm.hasAttribute('id') && elm.name) {\n-      return '[name=\"' + escapeSelector(elm.name) + '\"]';\n-    }\n-  },\n-  // Get any distinct classes (as long as there aren't more then 3 of them)\n+  // Get the three most distinct classes\n   getDistinctClass (elm, { distinctClassList }) {\n-    if (distinctClassList.length > 0 && distinctClassList.length < 3) {\n-      return '.' + distinctClassList.map(escapeSelector).join('.');\n-    }\n-  },\n-  // Get a selector that uses src/href props\n-  getFileRefProp (elm) {\n-    let attr;\n-    if (elm.hasAttribute('href')) {\n-      attr = 'href';\n-    } else if (elm.hasAttribute('src')) {\n-      attr = 'src';\n-    } else {\n-      return;\n-    }\n-    const friendlyUriEnd = axe.utils.getFriendlyUriEnd(elm.getAttribute(attr));\n-    if (friendlyUriEnd) {\n-      return '[' + attr + '$=\"' + encodeURI(friendlyUriEnd) + '\"]';\n-    }\n-  },\n-  // Get common node names\n-  getCommonName (elm, { nodeName, isCommonElm }) {\n-    if (isCommonElm) {\n-      return nodeName;\n+    if (distinctClassList.length > 0) {\n+      if (distinctClassList.length > 3) {\n+        distinctClassList = distinctClassList.slice(0, 3); \n+      }\n+      return '.' + distinctClassList.join('.');\n     }\n   }\n };\n \n /**\n- * Get an array of features (as CSS selectors) that describe an element\n+ * Return the base CSS selector for a given element\n+ * Class name selectors are faster than the tag but the tag is faster\n+ * than a universal selector\n  *\n- * By going down the list of most to least prominent element features,\n- * we attempt to find those features that a dev is most likely to\n- * recognize the element by (IDs, aria roles, custom element names, etc.)\n- */\n-function getElmFeatures (elm, featureCount) {\n+ * @param  {HTMLElement} elm The element to get the selector for\n+ * @return {String | Array[String]} Base CSS selector for the node\n+*/\n+function getBaseSelector(elm) {\n   if (typeof isXHTML === 'undefined') {\n     isXHTML = axe.utils.isXHTML(document);\n   }\n   const nodeName = escapeSelector(isXHTML?\n      elm.localName\n     :elm.nodeName.toLowerCase());\n-  const classList = Array.from(elm.classList) || [];\n-  // Collect some props we need to build the selector\n   const props = {\n     nodeName,\n-    classList,\n-    isCustomElm: nodeName.includes('-'),\n-    isCommonElm: commonNodes.includes(nodeName),\n     distinctClassList: getDistinctClassList(elm)\n   };\n+  var sel = createSelector.getDistinctClass(elm, props);\n+  if (!sel) {\n+    sel = nodeName;\n+  }\n+  return sel;\n+}\n \n-  return [\n-    // go through feature selectors in order of priority\n-    createSelector.getCustomElm,\n-    createSelector.getElmRoleProp,\n-    createSelector.getUncommonElm,\n-    createSelector.getElmNameProp,\n-    createSelector.getDistinctClass,\n-    createSelector.getFileRefProp,\n-    createSelector.getCommonName\n-  ].reduce((features, func) => {\n-    // As long as we haven't met our count, keep looking for features\n-    if (features.length === featureCount) {\n-      return features;\n-    }\n-\n-    const feature = func(elm, props);\n-    if (feature) {\n-      if (!feature[0].match(/[a-z]/)) {\n-        features.push(feature);\n-      } else {\n-        features.unshift(feature);\n+/**\n+ * Given a node and the statistics on attribute frequency on the page,\n+ * return all its uncommon attribute data sorted in order of decreasing uniqueness\n+ * @param {Element} node      The node\n+ * @param {Object} attData    The map of attributes to counts\n+ * @return {Array}            The sorted array of uncommon attribute data\n+ */\n+function uncommonAttributes(node, attData) {\n+  var retVal = [];\n+  \n+  if (node.attributes) {\n+    Array.from(node.attributes).filter(filterAttributes).forEach(function (at) {\n+      var atnv = getAttributeNameValue(node, at);\n+      \n+      if (atnv && attData[atnv] < axe._selectorData.tags[node.nodeName]) {\n+        retVal.push({\n+          aName: atnv,\n+          count: attData[atnv]\n+        });\n       }\n+    });\n+  }\n+  return retVal.sort(function (a, b) {\n+    return a.count > b.count;\n+  });\n+}\n+\n+/**\n+ * Get an array of features (as CSS selectors) that are likely to uniquely\n+ * describe an element\n+ *\n+ */\n+function getElmFeatures (elm) {\n+  let atts = uncommonAttributes(elm, axe._selectorData.attributes);\n+\n+  if (atts.length) {\n+    // If we have a unique attribute, use that\n+    if (atts[0].count === 1) {\n+      return ['[' + atts[0].aName + ']'];\n+    }\n+    if (atts.length > 3) {","path":"lib/core/utils/get-selector.js","position":367,"original_position":358,"commit_id":"b74e0de716d0d310cda7df025d92f06526c0d703","original_commit_id":"3937c18c39d848bb0e04868763f945fd83a0a437","user":{"login":"WilcoFiers","id":530687,"avatar_url":"https://avatars1.githubusercontent.com/u/530687?v=4","gravatar_id":"","url":"https://api.github.com/users/WilcoFiers","html_url":"https://github.com/WilcoFiers","followers_url":"https://api.github.com/users/WilcoFiers/followers","following_url":"https://api.github.com/users/WilcoFiers/following{/other_user}","gists_url":"https://api.github.com/users/WilcoFiers/gists{/gist_id}","starred_url":"https://api.github.com/users/WilcoFiers/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/WilcoFiers/subscriptions","organizations_url":"https://api.github.com/users/WilcoFiers/orgs","repos_url":"https://api.github.com/users/WilcoFiers/repos","events_url":"https://api.github.com/users/WilcoFiers/events{/privacy}","received_events_url":"https://api.github.com/users/WilcoFiers/received_events","type":"User","site_admin":false},"body":"--- If you want to try it yourself in an extension, you can just build one. I'm not sure we need public review on it.","created_at":"2018-02-23T12:57:25Z","updated_at":"2018-02-23T12:57:25Z","html_url":"https://github.com/dequelabs/axe-core/pull/731#discussion_r170244614","pull_request_url":"https://api.github.com/repos/dequelabs/axe-core/pulls/731","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/dequelabs/axe-core/pulls/comments/170244614"},"html":{"href":"https://github.com/dequelabs/axe-core/pull/731#discussion_r170244614"},"pull_request":{"href":"https://api.github.com/repos/dequelabs/axe-core/pulls/731"}},"in_reply_to_id":169598114},"pull_request":{"url":"https://api.github.com/repos/dequelabs/axe-core/pulls/731","id":168471806,"html_url":"https://github.com/dequelabs/axe-core/pull/731","diff_url":"https://github.com/dequelabs/axe-core/pull/731.diff","patch_url":"https://github.com/dequelabs/axe-core/pull/731.patch","issue_url":"https://api.github.com/repos/dequelabs/axe-core/issues/731","number":731,"state":"open","locked":false,"title":"fix(perf): Improve getSelector performance","user":{"login":"dylanb","id":79245,"avatar_url":"https://avatars3.githubusercontent.com/u/79245?v=4","gravatar_id":"","url":"https://api.github.com/users/dylanb","html_url":"https://github.com/dylanb","followers_url":"https://api.github.com/users/dylanb/followers","following_url":"https://api.github.com/users/dylanb/following{/other_user}","gists_url":"https://api.github.com/users/dylanb/gists{/gist_id}","starred_url":"https://api.github.com/users/dylanb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dylanb/subscriptions","organizations_url":"https://api.github.com/users/dylanb/orgs","repos_url":"https://api.github.com/users/dylanb/repos","events_url":"https://api.github.com/users/dylanb/events{/privacy}","received_events_url":"https://api.github.com/users/dylanb/received_events","type":"User","site_admin":false},"body":"@WilcoFiers Take a look at this commit, it decreases the `querySelectorAll` time on https://www.mlb.com/yankees/schedule/2018-03/list from ~17s to ~7s\r\n","created_at":"2018-02-11T20:17:08Z","updated_at":"2018-02-23T12:57:25Z","closed_at":null,"merged_at":null,"merge_commit_sha":"40540119252199815fbb38826e5d4a983b53aebd","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/dequelabs/axe-core/pulls/731/commits","review_comments_url":"https://api.github.com/repos/dequelabs/axe-core/pulls/731/comments","review_comment_url":"https://api.github.com/repos/dequelabs/axe-core/pulls/comments{/number}","comments_url":"https://api.github.com/repos/dequelabs/axe-core/issues/731/comments","statuses_url":"https://api.github.com/repos/dequelabs/axe-core/statuses/b74e0de716d0d310cda7df025d92f06526c0d703","head":{"label":"dequelabs:selectperf","ref":"selectperf","sha":"b74e0de716d0d310cda7df025d92f06526c0d703","user":{"login":"dequelabs","id":4094299,"avatar_url":"https://avatars0.githubusercontent.com/u/4094299?v=4","gravatar_id":"","url":"https://api.github.com/users/dequelabs","html_url":"https://github.com/dequelabs","followers_url":"https://api.github.com/users/dequelabs/followers","following_url":"https://api.github.com/users/dequelabs/following{/other_user}","gists_url":"https://api.github.com/users/dequelabs/gists{/gist_id}","starred_url":"https://api.github.com/users/dequelabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dequelabs/subscriptions","organizations_url":"https://api.github.com/users/dequelabs/orgs","repos_url":"https://api.github.com/users/dequelabs/repos","events_url":"https://api.github.com/users/dequelabs/events{/privacy}","received_events_url":"https://api.github.com/users/dequelabs/received_events","type":"Organization","site_admin":false},"repo":{"id":37205440,"name":"axe-core","full_name":"dequelabs/axe-core","owner":{"login":"dequelabs","id":4094299,"avatar_url":"https://avatars0.githubusercontent.com/u/4094299?v=4","gravatar_id":"","url":"https://api.github.com/users/dequelabs","html_url":"https://github.com/dequelabs","followers_url":"https://api.github.com/users/dequelabs/followers","following_url":"https://api.github.com/users/dequelabs/following{/other_user}","gists_url":"https://api.github.com/users/dequelabs/gists{/gist_id}","starred_url":"https://api.github.com/users/dequelabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dequelabs/subscriptions","organizations_url":"https://api.github.com/users/dequelabs/orgs","repos_url":"https://api.github.com/users/dequelabs/repos","events_url":"https://api.github.com/users/dequelabs/events{/privacy}","received_events_url":"https://api.github.com/users/dequelabs/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/dequelabs/axe-core","description":"Accessibility engine for automated Web UI testing","fork":false,"url":"https://api.github.com/repos/dequelabs/axe-core","forks_url":"https://api.github.com/repos/dequelabs/axe-core/forks","keys_url":"https://api.github.com/repos/dequelabs/axe-core/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dequelabs/axe-core/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dequelabs/axe-core/teams","hooks_url":"https://api.github.com/repos/dequelabs/axe-core/hooks","issue_events_url":"https://api.github.com/repos/dequelabs/axe-core/issues/events{/number}","events_url":"https://api.github.com/repos/dequelabs/axe-core/events","assignees_url":"https://api.github.com/repos/dequelabs/axe-core/assignees{/user}","branches_url":"https://api.github.com/repos/dequelabs/axe-core/branches{/branch}","tags_url":"https://api.github.com/repos/dequelabs/axe-core/tags","blobs_url":"https://api.github.com/repos/dequelabs/axe-core/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dequelabs/axe-core/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dequelabs/axe-core/git/refs{/sha}","trees_url":"https://api.github.com/repos/dequelabs/axe-core/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dequelabs/axe-core/statuses/{sha}","languages_url":"https://api.github.com/repos/dequelabs/axe-core/languages","stargazers_url":"https://api.github.com/repos/dequelabs/axe-core/stargazers","contributors_url":"https://api.github.com/repos/dequelabs/axe-core/contributors","subscribers_url":"https://api.github.com/repos/dequelabs/axe-core/subscribers","subscription_url":"https://api.github.com/repos/dequelabs/axe-core/subscription","commits_url":"https://api.github.com/repos/dequelabs/axe-core/commits{/sha}","git_commits_url":"https://api.github.com/repos/dequelabs/axe-core/git/commits{/sha}","comments_url":"https://api.github.com/repos/dequelabs/axe-core/comments{/number}","issue_comment_url":"https://api.github.com/repos/dequelabs/axe-core/issues/comments{/number}","contents_url":"https://api.github.com/repos/dequelabs/axe-core/contents/{+path}","compare_url":"https://api.github.com/repos/dequelabs/axe-core/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dequelabs/axe-core/merges","archive_url":"https://api.github.com/repos/dequelabs/axe-core/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dequelabs/axe-core/downloads","issues_url":"https://api.github.com/repos/dequelabs/axe-core/issues{/number}","pulls_url":"https://api.github.com/repos/dequelabs/axe-core/pulls{/number}","milestones_url":"https://api.github.com/repos/dequelabs/axe-core/milestones{/number}","notifications_url":"https://api.github.com/repos/dequelabs/axe-core/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dequelabs/axe-core/labels{/name}","releases_url":"https://api.github.com/repos/dequelabs/axe-core/releases{/id}","deployments_url":"https://api.github.com/repos/dequelabs/axe-core/deployments","created_at":"2015-06-10T15:26:45Z","updated_at":"2018-02-23T04:26:41Z","pushed_at":"2018-02-23T11:50:14Z","git_url":"git://github.com/dequelabs/axe-core.git","ssh_url":"git@github.com:dequelabs/axe-core.git","clone_url":"https://github.com/dequelabs/axe-core.git","svn_url":"https://github.com/dequelabs/axe-core","homepage":"https://www.axe-core.org/","size":3705,"stargazers_count":1348,"watchers_count":1348,"language":"JavaScript","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":193,"mirror_url":null,"archived":false,"open_issues_count":105,"license":{"key":"mpl-2.0","name":"Mozilla Public License 2.0","spdx_id":"MPL-2.0","url":"https://api.github.com/licenses/mpl-2.0"},"forks":193,"open_issues":105,"watchers":1348,"default_branch":"develop"}},"base":{"label":"dequelabs:develop","ref":"develop","sha":"f758dd54be4fb571cf80913b7b357904a7cb3f20","user":{"login":"dequelabs","id":4094299,"avatar_url":"https://avatars0.githubusercontent.com/u/4094299?v=4","gravatar_id":"","url":"https://api.github.com/users/dequelabs","html_url":"https://github.com/dequelabs","followers_url":"https://api.github.com/users/dequelabs/followers","following_url":"https://api.github.com/users/dequelabs/following{/other_user}","gists_url":"https://api.github.com/users/dequelabs/gists{/gist_id}","starred_url":"https://api.github.com/users/dequelabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dequelabs/subscriptions","organizations_url":"https://api.github.com/users/dequelabs/orgs","repos_url":"https://api.github.com/users/dequelabs/repos","events_url":"https://api.github.com/users/dequelabs/events{/privacy}","received_events_url":"https://api.github.com/users/dequelabs/received_events","type":"Organization","site_admin":false},"repo":{"id":37205440,"name":"axe-core","full_name":"dequelabs/axe-core","owner":{"login":"dequelabs","id":4094299,"avatar_url":"https://avatars0.githubusercontent.com/u/4094299?v=4","gravatar_id":"","url":"https://api.github.com/users/dequelabs","html_url":"https://github.com/dequelabs","followers_url":"https://api.github.com/users/dequelabs/followers","following_url":"https://api.github.com/users/dequelabs/following{/other_user}","gists_url":"https://api.github.com/users/dequelabs/gists{/gist_id}","starred_url":"https://api.github.com/users/dequelabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dequelabs/subscriptions","organizations_url":"https://api.github.com/users/dequelabs/orgs","repos_url":"https://api.github.com/users/dequelabs/repos","events_url":"https://api.github.com/users/dequelabs/events{/privacy}","received_events_url":"https://api.github.com/users/dequelabs/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/dequelabs/axe-core","description":"Accessibility engine for automated Web UI testing","fork":false,"url":"https://api.github.com/repos/dequelabs/axe-core","forks_url":"https://api.github.com/repos/dequelabs/axe-core/forks","keys_url":"https://api.github.com/repos/dequelabs/axe-core/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dequelabs/axe-core/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dequelabs/axe-core/teams","hooks_url":"https://api.github.com/repos/dequelabs/axe-core/hooks","issue_events_url":"https://api.github.com/repos/dequelabs/axe-core/issues/events{/number}","events_url":"https://api.github.com/repos/dequelabs/axe-core/events","assignees_url":"https://api.github.com/repos/dequelabs/axe-core/assignees{/user}","branches_url":"https://api.github.com/repos/dequelabs/axe-core/branches{/branch}","tags_url":"https://api.github.com/repos/dequelabs/axe-core/tags","blobs_url":"https://api.github.com/repos/dequelabs/axe-core/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dequelabs/axe-core/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dequelabs/axe-core/git/refs{/sha}","trees_url":"https://api.github.com/repos/dequelabs/axe-core/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dequelabs/axe-core/statuses/{sha}","languages_url":"https://api.github.com/repos/dequelabs/axe-core/languages","stargazers_url":"https://api.github.com/repos/dequelabs/axe-core/stargazers","contributors_url":"https://api.github.com/repos/dequelabs/axe-core/contributors","subscribers_url":"https://api.github.com/repos/dequelabs/axe-core/subscribers","subscription_url":"https://api.github.com/repos/dequelabs/axe-core/subscription","commits_url":"https://api.github.com/repos/dequelabs/axe-core/commits{/sha}","git_commits_url":"https://api.github.com/repos/dequelabs/axe-core/git/commits{/sha}","comments_url":"https://api.github.com/repos/dequelabs/axe-core/comments{/number}","issue_comment_url":"https://api.github.com/repos/dequelabs/axe-core/issues/comments{/number}","contents_url":"https://api.github.com/repos/dequelabs/axe-core/contents/{+path}","compare_url":"https://api.github.com/repos/dequelabs/axe-core/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dequelabs/axe-core/merges","archive_url":"https://api.github.com/repos/dequelabs/axe-core/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dequelabs/axe-core/downloads","issues_url":"https://api.github.com/repos/dequelabs/axe-core/issues{/number}","pulls_url":"https://api.github.com/repos/dequelabs/axe-core/pulls{/number}","milestones_url":"https://api.github.com/repos/dequelabs/axe-core/milestones{/number}","notifications_url":"https://api.github.com/repos/dequelabs/axe-core/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dequelabs/axe-core/labels{/name}","releases_url":"https://api.github.com/repos/dequelabs/axe-core/releases{/id}","deployments_url":"https://api.github.com/repos/dequelabs/axe-core/deployments","created_at":"2015-06-10T15:26:45Z","updated_at":"2018-02-23T04:26:41Z","pushed_at":"2018-02-23T11:50:14Z","git_url":"git://github.com/dequelabs/axe-core.git","ssh_url":"git@github.com:dequelabs/axe-core.git","clone_url":"https://github.com/dequelabs/axe-core.git","svn_url":"https://github.com/dequelabs/axe-core","homepage":"https://www.axe-core.org/","size":3705,"stargazers_count":1348,"watchers_count":1348,"language":"JavaScript","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":193,"mirror_url":null,"archived":false,"open_issues_count":105,"license":{"key":"mpl-2.0","name":"Mozilla Public License 2.0","spdx_id":"MPL-2.0","url":"https://api.github.com/licenses/mpl-2.0"},"forks":193,"open_issues":105,"watchers":1348,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/dequelabs/axe-core/pulls/731"},"html":{"href":"https://github.com/dequelabs/axe-core/pull/731"},"issue":{"href":"https://api.github.com/repos/dequelabs/axe-core/issues/731"},"comments":{"href":"https://api.github.com/repos/dequelabs/axe-core/issues/731/comments"},"review_comments":{"href":"https://api.github.com/repos/dequelabs/axe-core/pulls/731/comments"},"review_comment":{"href":"https://api.github.com/repos/dequelabs/axe-core/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/dequelabs/axe-core/pulls/731/commits"},"statuses":{"href":"https://api.github.com/repos/dequelabs/axe-core/statuses/b74e0de716d0d310cda7df025d92f06526c0d703"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"WilcoFiers"}}