暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

mega-dropdown.js 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. const TIMEOUT = 150
  2. class MegaDropdown {
  3. constructor(element, options = {}) {
  4. this._onHover = options.trigger === 'hover' || element.getAttribute('data-trigger') === 'hover'
  5. this._container = MegaDropdown._findParent(element, 'mega-dropdown')
  6. if (!this._container) return
  7. this._menu = this._container.querySelector('.dropdown-toggle ~ .dropdown-menu')
  8. if (!this._menu) return
  9. element.setAttribute('aria-expanded', 'false')
  10. this._el = element
  11. this._bindEvents()
  12. }
  13. open() {
  14. if (this._timeout) {
  15. clearTimeout(this._timeout)
  16. this._timeout = null
  17. }
  18. if (this._focusTimeout) {
  19. clearTimeout(this._focusTimeout)
  20. this._focusTimeout = null
  21. }
  22. if (this._el.getAttribute('aria-expanded') !== 'true') {
  23. this._triggerEvent('show')
  24. this._container.classList.add('show')
  25. this._menu.classList.add('show')
  26. this._el.setAttribute('aria-expanded', 'true')
  27. this._el.focus()
  28. this._triggerEvent('shown')
  29. }
  30. }
  31. close(force) {
  32. if (this._timeout) {
  33. clearTimeout(this._timeout)
  34. this._timeout = null
  35. }
  36. if (this._focusTimeout) {
  37. clearTimeout(this._focusTimeout)
  38. this._focusTimeout = null
  39. }
  40. if (this._onHover && !force) {
  41. this._timeout = setTimeout(() => {
  42. if (this._timeout) {
  43. clearTimeout(this._timeout)
  44. this._timeout = null
  45. }
  46. this._close()
  47. }, TIMEOUT)
  48. } else {
  49. this._close()
  50. }
  51. }
  52. toggle() {
  53. // eslint-disable-next-line no-unused-expressions
  54. this._el.getAttribute('aria-expanded') === 'true' ? this.close(true) : this.open()
  55. }
  56. destroy() {
  57. this._unbindEvents()
  58. this._el = null
  59. if (this._timeout) {
  60. clearTimeout(this._timeout)
  61. this._timeout = null
  62. }
  63. if (this._focusTimeout) {
  64. clearTimeout(this._focusTimeout)
  65. this._focusTimeout = null
  66. }
  67. }
  68. _close() {
  69. if (this._el.getAttribute('aria-expanded') === 'true') {
  70. this._triggerEvent('hide')
  71. this._container.classList.remove('show')
  72. this._menu.classList.remove('show')
  73. this._el.setAttribute('aria-expanded', 'false')
  74. this._triggerEvent('hidden')
  75. }
  76. }
  77. _bindEvents() {
  78. this._elClickEvnt = e => {
  79. e.preventDefault()
  80. this.toggle()
  81. }
  82. this._el.addEventListener('click', this._elClickEvnt)
  83. this._bodyClickEvnt = e => {
  84. if (!this._container.contains(e.target) && this._container.classList.contains('show')) {
  85. this.close(true)
  86. }
  87. }
  88. document.body.addEventListener('click', this._bodyClickEvnt, true)
  89. this._menuClickEvnt = e => {
  90. if (e.target.classList.contains('mega-dropdown-link')) {
  91. this.close(true)
  92. }
  93. }
  94. this._menu.addEventListener('click', this._menuClickEvnt, true)
  95. this._focusoutEvnt = () => {
  96. if (this._focusTimeout) {
  97. clearTimeout(this._focusTimeout)
  98. this._focusTimeout = null
  99. }
  100. if (this._el.getAttribute('aria-expanded') !== 'true') return
  101. this._focusTimeout = setTimeout(() => {
  102. if (
  103. document.activeElement.tagName.toUpperCase() !== 'BODY' &&
  104. MegaDropdown._findParent(document.activeElement, 'mega-dropdown') !== this._container
  105. ) {
  106. this.close(true)
  107. }
  108. }, 100)
  109. }
  110. this._container.addEventListener('focusout', this._focusoutEvnt, true)
  111. if (this._onHover) {
  112. this._enterEvnt = () => {
  113. if (window.getComputedStyle(this._menu, null).getPropertyValue('position') === 'static') return
  114. this.open()
  115. }
  116. this._leaveEvnt = () => {
  117. if (window.getComputedStyle(this._menu, null).getPropertyValue('position') === 'static') return
  118. this.close()
  119. }
  120. this._el.addEventListener('mouseenter', this._enterEvnt)
  121. this._menu.addEventListener('mouseenter', this._enterEvnt)
  122. this._el.addEventListener('mouseleave', this._leaveEvnt)
  123. this._menu.addEventListener('mouseleave', this._leaveEvnt)
  124. }
  125. }
  126. _unbindEvents() {
  127. if (this._elClickEvnt) {
  128. this._el.removeEventListener('click', this._elClickEvnt)
  129. this._elClickEvnt = null
  130. }
  131. if (this._bodyClickEvnt) {
  132. document.body.removeEventListener('click', this._bodyClickEvnt, true)
  133. this._bodyClickEvnt = null
  134. }
  135. if (this._menuClickEvnt) {
  136. this._menu.removeEventListener('click', this._menuClickEvnt, true)
  137. this._menuClickEvnt = null
  138. }
  139. if (this._focusoutEvnt) {
  140. this._container.removeEventListener('focusout', this._focusoutEvnt, true)
  141. this._focusoutEvnt = null
  142. }
  143. if (this._enterEvnt) {
  144. this._el.removeEventListener('mouseenter', this._enterEvnt)
  145. this._menu.removeEventListener('mouseenter', this._enterEvnt)
  146. this._enterEvnt = null
  147. }
  148. if (this._leaveEvnt) {
  149. this._el.removeEventListener('mouseleave', this._leaveEvnt)
  150. this._menu.removeEventListener('mouseleave', this._leaveEvnt)
  151. this._leaveEvnt = null
  152. }
  153. }
  154. static _findParent(el, cls) {
  155. if (el.tagName.toUpperCase() === 'BODY') return null
  156. el = el.parentNode
  157. while (el.tagName.toUpperCase() !== 'BODY' && !el.classList.contains(cls)) {
  158. el = el.parentNode
  159. }
  160. return el.tagName.toUpperCase() !== 'BODY' ? el : null
  161. }
  162. _triggerEvent(event) {
  163. if (document.createEvent) {
  164. let customEvent
  165. if (typeof Event === 'function') {
  166. customEvent = new Event(event)
  167. } else {
  168. customEvent = document.createEvent('Event')
  169. customEvent.initEvent(event, false, true)
  170. }
  171. this._container.dispatchEvent(customEvent)
  172. } else {
  173. this._container.fireEvent(`on${event}`, document.createEventObject())
  174. }
  175. }
  176. }
  177. window.MegaDropdown = MegaDropdown
  178. export { MegaDropdown }