CSSCore.js.flow 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. /**
  2. * Copyright (c) 2013-present, Facebook, Inc.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * @providesModule CSSCore
  8. * @typechecks
  9. */
  10. const invariant = require('./invariant');
  11. /**
  12. * The CSSCore module specifies the API (and implements most of the methods)
  13. * that should be used when dealing with the display of elements (via their
  14. * CSS classes and visibility on screen. It is an API focused on mutating the
  15. * display and not reading it as no logical state should be encoded in the
  16. * display of elements.
  17. */
  18. /* Slow implementation for browsers that don't natively support .matches() */
  19. function matchesSelector_SLOW(element, selector) {
  20. let root = element;
  21. while (root.parentNode) {
  22. root = root.parentNode;
  23. }
  24. const all = root.querySelectorAll(selector);
  25. return Array.prototype.indexOf.call(all, element) !== -1;
  26. }
  27. const CSSCore = {
  28. /**
  29. * Adds the class passed in to the element if it doesn't already have it.
  30. *
  31. * @param {DOMElement} element the element to set the class on
  32. * @param {string} className the CSS className
  33. * @return {DOMElement} the element passed in
  34. */
  35. addClass: function (element, className) {
  36. invariant(!/\s/.test(className), 'CSSCore.addClass takes only a single class name. "%s" contains ' + 'multiple classes.', className);
  37. if (className) {
  38. if (element.classList) {
  39. element.classList.add(className);
  40. } else if (!CSSCore.hasClass(element, className)) {
  41. element.className = element.className + ' ' + className;
  42. }
  43. }
  44. return element;
  45. },
  46. /**
  47. * Removes the class passed in from the element
  48. *
  49. * @param {DOMElement} element the element to set the class on
  50. * @param {string} className the CSS className
  51. * @return {DOMElement} the element passed in
  52. */
  53. removeClass: function (element, className) {
  54. invariant(!/\s/.test(className), 'CSSCore.removeClass takes only a single class name. "%s" contains ' + 'multiple classes.', className);
  55. if (className) {
  56. if (element.classList) {
  57. element.classList.remove(className);
  58. } else if (CSSCore.hasClass(element, className)) {
  59. element.className = element.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)', 'g'), '$1').replace(/\s+/g, ' ') // multiple spaces to one
  60. .replace(/^\s*|\s*$/g, ''); // trim the ends
  61. }
  62. }
  63. return element;
  64. },
  65. /**
  66. * Helper to add or remove a class from an element based on a condition.
  67. *
  68. * @param {DOMElement} element the element to set the class on
  69. * @param {string} className the CSS className
  70. * @param {*} bool condition to whether to add or remove the class
  71. * @return {DOMElement} the element passed in
  72. */
  73. conditionClass: function (element, className, bool) {
  74. return (bool ? CSSCore.addClass : CSSCore.removeClass)(element, className);
  75. },
  76. /**
  77. * Tests whether the element has the class specified.
  78. *
  79. * @param {DOMNode|DOMWindow} element the element to check the class on
  80. * @param {string} className the CSS className
  81. * @return {boolean} true if the element has the class, false if not
  82. */
  83. hasClass: function (element, className) {
  84. invariant(!/\s/.test(className), 'CSS.hasClass takes only a single class name.');
  85. if (element.classList) {
  86. return !!className && element.classList.contains(className);
  87. }
  88. return (' ' + element.className + ' ').indexOf(' ' + className + ' ') > -1;
  89. },
  90. /**
  91. * Tests whether the element matches the selector specified
  92. *
  93. * @param {DOMNode|DOMWindow} element the element that we are querying
  94. * @param {string} selector the CSS selector
  95. * @return {boolean} true if the element matches the selector, false if not
  96. */
  97. matchesSelector: function (element, selector) {
  98. const matchesImpl = element.matches || element.webkitMatchesSelector || element.mozMatchesSelector || element.msMatchesSelector || (s => matchesSelector_SLOW(element, s));
  99. return matchesImpl.call(element, selector);
  100. }
  101. };
  102. module.exports = CSSCore;