component-classes.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /**
  2. * source by `component-classes`
  3. * https://github.com/component/classes.git
  4. */
  5. import indexOf from 'lodash-es/indexOf';
  6. /**
  7. * Whitespace regexp.
  8. */
  9. const re = /\s+/;
  10. export class ClassList {
  11. constructor(el) {
  12. if (!el || !el.nodeType) {
  13. throw new Error('A DOM element reference is required');
  14. }
  15. this.el = el;
  16. this.list = el.classList;
  17. }
  18. array() {
  19. const className = this.el.getAttribute('class') || '';
  20. const str = className.replace(/^\s+|\s+$/g, '');
  21. const arr = str.split(re);
  22. if ('' === arr[0]) arr.shift();
  23. return arr;
  24. }
  25. /**
  26. * Add class `name` if not already present.
  27. *
  28. * @param {String} name
  29. * @return {ClassList}
  30. * @api public
  31. */
  32. add(name) {
  33. // classList
  34. if (this.list) {
  35. this.list.add(name);
  36. return this;
  37. }
  38. // fallback
  39. const arr = this.array();
  40. const i = indexOf(arr, name);
  41. if (!~i) arr.push(name);
  42. this.el.className = arr.join(' ');
  43. return this;
  44. }
  45. /**
  46. * Remove class `name` when present, or
  47. * pass a regular expression to remove
  48. * any which match.
  49. *
  50. * @param {String|RegExp} name
  51. * @return {ClassList}
  52. * @api public
  53. */
  54. remove(name) {
  55. if ('[object RegExp]' === toString.call(name)) {
  56. return this._removeMatching(name);
  57. }
  58. // classList
  59. if (this.list) {
  60. this.list.remove(name);
  61. return this;
  62. }
  63. // fallback
  64. const arr = this.array();
  65. const i = indexOf(arr, name);
  66. if (~i) arr.splice(i, 1);
  67. this.el.className = arr.join(' ');
  68. return this;
  69. }
  70. /**
  71. * Remove all classes matching `re`.
  72. *
  73. * @param {RegExp} re
  74. * @return {ClassList}
  75. * @api private
  76. */
  77. _removeMatching(re) {
  78. const arr = this.array();
  79. for (let i = 0; i < arr.length; i++) {
  80. if (re.test(arr[i])) {
  81. this.remove(arr[i]);
  82. }
  83. }
  84. return this;
  85. }
  86. /**
  87. * Toggle class `name`, can force state via `force`.
  88. *
  89. * For browsers that support classList, but do not support `force` yet,
  90. * the mistake will be detected and corrected.
  91. *
  92. * @param {String} name
  93. * @param {Boolean} force
  94. * @return {ClassList}
  95. * @api public
  96. */
  97. toggle(name, force) {
  98. // classList
  99. if (this.list) {
  100. if ('undefined' !== typeof force) {
  101. if (force !== this.list.toggle(name, force)) {
  102. this.list.toggle(name); // toggle again to correct
  103. }
  104. } else {
  105. this.list.toggle(name);
  106. }
  107. return this;
  108. }
  109. // fallback
  110. if ('undefined' !== typeof force) {
  111. if (!force) {
  112. this.remove(name);
  113. } else {
  114. this.add(name);
  115. }
  116. } else {
  117. if (this.has(name)) {
  118. this.remove(name);
  119. } else {
  120. this.add(name);
  121. }
  122. }
  123. return this;
  124. }
  125. /**
  126. * Check if class `name` is present.
  127. *
  128. * @param {String} name
  129. * @api public
  130. */
  131. has(name) {
  132. return this.list ? this.list.contains(name) : !!~indexOf(this.array(), name);
  133. }
  134. /**
  135. * Check if class `name` is present.
  136. *
  137. * @param {String} name
  138. * @api public
  139. */
  140. contains(name) {
  141. return this.has(name);
  142. }
  143. }
  144. /**
  145. * Wrap `el` in a `ClassList`.
  146. *
  147. * @param {Element} el
  148. * @return {ClassList}
  149. * @api public
  150. */
  151. export default function (el) {
  152. return new ClassList(el);
  153. }