index.js 3.3 KB

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