is-skip-link.js 1.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
  1. import cache from '../../core/base/cache';
  2. import { querySelectorAll } from '../../core/utils';
  3. // test for hrefs that start with # or /# (for angular)
  4. const isInternalLinkRegex = /^\/?#[^/!]/;
  5. /**
  6. * Determines if element is a skip link
  7. * @method isSkipLink
  8. * @memberof axe.commons.dom
  9. * @instance
  10. * @param {Element} element
  11. * @return {Boolean}
  12. */
  13. function isSkipLink(element) {
  14. if (!isInternalLinkRegex.test(element.getAttribute('href'))) {
  15. return false;
  16. }
  17. let firstPageLink;
  18. if (typeof cache.get('firstPageLink') !== 'undefined') {
  19. firstPageLink = cache.get('firstPageLink');
  20. } else {
  21. // define a skip link as any anchor element whose href starts with `#...`
  22. // and which precedes the first anchor element whose href doesn't start
  23. // with `#...` (that is, a link to a page)
  24. firstPageLink = querySelectorAll(
  25. // TODO: es-module-_tree
  26. axe._tree,
  27. 'a:not([href^="#"]):not([href^="/#"]):not([href^="javascript"])'
  28. )[0];
  29. // null will signify no first page link
  30. cache.set('firstPageLink', firstPageLink || null);
  31. }
  32. // if there are no page links then all all links will need to be
  33. // considered as skip links
  34. if (!firstPageLink) {
  35. return true;
  36. }
  37. return (
  38. element.compareDocumentPosition(firstPageLink.actualNode) ===
  39. element.DOCUMENT_POSITION_FOLLOWING
  40. );
  41. }
  42. export default isSkipLink;