Layout von Websiten mit Bootstrap und Foundation
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

collapse.js 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. /**
  2. * --------------------------------------------------------------------------
  3. * Bootstrap (v4.5.0): collapse.js
  4. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
  5. * --------------------------------------------------------------------------
  6. */
  7. import $ from 'jquery'
  8. import Util from './util'
  9. /**
  10. * ------------------------------------------------------------------------
  11. * Constants
  12. * ------------------------------------------------------------------------
  13. */
  14. const NAME = 'collapse'
  15. const VERSION = '4.5.0'
  16. const DATA_KEY = 'bs.collapse'
  17. const EVENT_KEY = `.${DATA_KEY}`
  18. const DATA_API_KEY = '.data-api'
  19. const JQUERY_NO_CONFLICT = $.fn[NAME]
  20. const Default = {
  21. toggle : true,
  22. parent : ''
  23. }
  24. const DefaultType = {
  25. toggle : 'boolean',
  26. parent : '(string|element)'
  27. }
  28. const EVENT_SHOW = `show${EVENT_KEY}`
  29. const EVENT_SHOWN = `shown${EVENT_KEY}`
  30. const EVENT_HIDE = `hide${EVENT_KEY}`
  31. const EVENT_HIDDEN = `hidden${EVENT_KEY}`
  32. const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
  33. const CLASS_NAME_SHOW = 'show'
  34. const CLASS_NAME_COLLAPSE = 'collapse'
  35. const CLASS_NAME_COLLAPSING = 'collapsing'
  36. const CLASS_NAME_COLLAPSED = 'collapsed'
  37. const DIMENSION_WIDTH = 'width'
  38. const DIMENSION_HEIGHT = 'height'
  39. const SELECTOR_ACTIVES = '.show, .collapsing'
  40. const SELECTOR_DATA_TOGGLE = '[data-toggle="collapse"]'
  41. /**
  42. * ------------------------------------------------------------------------
  43. * Class Definition
  44. * ------------------------------------------------------------------------
  45. */
  46. class Collapse {
  47. constructor(element, config) {
  48. this._isTransitioning = false
  49. this._element = element
  50. this._config = this._getConfig(config)
  51. this._triggerArray = [].slice.call(document.querySelectorAll(
  52. `[data-toggle="collapse"][href="#${element.id}"],` +
  53. `[data-toggle="collapse"][data-target="#${element.id}"]`
  54. ))
  55. const toggleList = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLE))
  56. for (let i = 0, len = toggleList.length; i < len; i++) {
  57. const elem = toggleList[i]
  58. const selector = Util.getSelectorFromElement(elem)
  59. const filterElement = [].slice.call(document.querySelectorAll(selector))
  60. .filter((foundElem) => foundElem === element)
  61. if (selector !== null && filterElement.length > 0) {
  62. this._selector = selector
  63. this._triggerArray.push(elem)
  64. }
  65. }
  66. this._parent = this._config.parent ? this._getParent() : null
  67. if (!this._config.parent) {
  68. this._addAriaAndCollapsedClass(this._element, this._triggerArray)
  69. }
  70. if (this._config.toggle) {
  71. this.toggle()
  72. }
  73. }
  74. // Getters
  75. static get VERSION() {
  76. return VERSION
  77. }
  78. static get Default() {
  79. return Default
  80. }
  81. // Public
  82. toggle() {
  83. if ($(this._element).hasClass(CLASS_NAME_SHOW)) {
  84. this.hide()
  85. } else {
  86. this.show()
  87. }
  88. }
  89. show() {
  90. if (this._isTransitioning ||
  91. $(this._element).hasClass(CLASS_NAME_SHOW)) {
  92. return
  93. }
  94. let actives
  95. let activesData
  96. if (this._parent) {
  97. actives = [].slice.call(this._parent.querySelectorAll(SELECTOR_ACTIVES))
  98. .filter((elem) => {
  99. if (typeof this._config.parent === 'string') {
  100. return elem.getAttribute('data-parent') === this._config.parent
  101. }
  102. return elem.classList.contains(CLASS_NAME_COLLAPSE)
  103. })
  104. if (actives.length === 0) {
  105. actives = null
  106. }
  107. }
  108. if (actives) {
  109. activesData = $(actives).not(this._selector).data(DATA_KEY)
  110. if (activesData && activesData._isTransitioning) {
  111. return
  112. }
  113. }
  114. const startEvent = $.Event(EVENT_SHOW)
  115. $(this._element).trigger(startEvent)
  116. if (startEvent.isDefaultPrevented()) {
  117. return
  118. }
  119. if (actives) {
  120. Collapse._jQueryInterface.call($(actives).not(this._selector), 'hide')
  121. if (!activesData) {
  122. $(actives).data(DATA_KEY, null)
  123. }
  124. }
  125. const dimension = this._getDimension()
  126. $(this._element)
  127. .removeClass(CLASS_NAME_COLLAPSE)
  128. .addClass(CLASS_NAME_COLLAPSING)
  129. this._element.style[dimension] = 0
  130. if (this._triggerArray.length) {
  131. $(this._triggerArray)
  132. .removeClass(CLASS_NAME_COLLAPSED)
  133. .attr('aria-expanded', true)
  134. }
  135. this.setTransitioning(true)
  136. const complete = () => {
  137. $(this._element)
  138. .removeClass(CLASS_NAME_COLLAPSING)
  139. .addClass(`${CLASS_NAME_COLLAPSE} ${CLASS_NAME_SHOW}`)
  140. this._element.style[dimension] = ''
  141. this.setTransitioning(false)
  142. $(this._element).trigger(EVENT_SHOWN)
  143. }
  144. const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)
  145. const scrollSize = `scroll${capitalizedDimension}`
  146. const transitionDuration = Util.getTransitionDurationFromElement(this._element)
  147. $(this._element)
  148. .one(Util.TRANSITION_END, complete)
  149. .emulateTransitionEnd(transitionDuration)
  150. this._element.style[dimension] = `${this._element[scrollSize]}px`
  151. }
  152. hide() {
  153. if (this._isTransitioning ||
  154. !$(this._element).hasClass(CLASS_NAME_SHOW)) {
  155. return
  156. }
  157. const startEvent = $.Event(EVENT_HIDE)
  158. $(this._element).trigger(startEvent)
  159. if (startEvent.isDefaultPrevented()) {
  160. return
  161. }
  162. const dimension = this._getDimension()
  163. this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`
  164. Util.reflow(this._element)
  165. $(this._element)
  166. .addClass(CLASS_NAME_COLLAPSING)
  167. .removeClass(`${CLASS_NAME_COLLAPSE} ${CLASS_NAME_SHOW}`)
  168. const triggerArrayLength = this._triggerArray.length
  169. if (triggerArrayLength > 0) {
  170. for (let i = 0; i < triggerArrayLength; i++) {
  171. const trigger = this._triggerArray[i]
  172. const selector = Util.getSelectorFromElement(trigger)
  173. if (selector !== null) {
  174. const $elem = $([].slice.call(document.querySelectorAll(selector)))
  175. if (!$elem.hasClass(CLASS_NAME_SHOW)) {
  176. $(trigger).addClass(CLASS_NAME_COLLAPSED)
  177. .attr('aria-expanded', false)
  178. }
  179. }
  180. }
  181. }
  182. this.setTransitioning(true)
  183. const complete = () => {
  184. this.setTransitioning(false)
  185. $(this._element)
  186. .removeClass(CLASS_NAME_COLLAPSING)
  187. .addClass(CLASS_NAME_COLLAPSE)
  188. .trigger(EVENT_HIDDEN)
  189. }
  190. this._element.style[dimension] = ''
  191. const transitionDuration = Util.getTransitionDurationFromElement(this._element)
  192. $(this._element)
  193. .one(Util.TRANSITION_END, complete)
  194. .emulateTransitionEnd(transitionDuration)
  195. }
  196. setTransitioning(isTransitioning) {
  197. this._isTransitioning = isTransitioning
  198. }
  199. dispose() {
  200. $.removeData(this._element, DATA_KEY)
  201. this._config = null
  202. this._parent = null
  203. this._element = null
  204. this._triggerArray = null
  205. this._isTransitioning = null
  206. }
  207. // Private
  208. _getConfig(config) {
  209. config = {
  210. ...Default,
  211. ...config
  212. }
  213. config.toggle = Boolean(config.toggle) // Coerce string values
  214. Util.typeCheckConfig(NAME, config, DefaultType)
  215. return config
  216. }
  217. _getDimension() {
  218. const hasWidth = $(this._element).hasClass(DIMENSION_WIDTH)
  219. return hasWidth ? DIMENSION_WIDTH : DIMENSION_HEIGHT
  220. }
  221. _getParent() {
  222. let parent
  223. if (Util.isElement(this._config.parent)) {
  224. parent = this._config.parent
  225. // It's a jQuery object
  226. if (typeof this._config.parent.jquery !== 'undefined') {
  227. parent = this._config.parent[0]
  228. }
  229. } else {
  230. parent = document.querySelector(this._config.parent)
  231. }
  232. const selector = `[data-toggle="collapse"][data-parent="${this._config.parent}"]`
  233. const children = [].slice.call(parent.querySelectorAll(selector))
  234. $(children).each((i, element) => {
  235. this._addAriaAndCollapsedClass(
  236. Collapse._getTargetFromElement(element),
  237. [element]
  238. )
  239. })
  240. return parent
  241. }
  242. _addAriaAndCollapsedClass(element, triggerArray) {
  243. const isOpen = $(element).hasClass(CLASS_NAME_SHOW)
  244. if (triggerArray.length) {
  245. $(triggerArray)
  246. .toggleClass(CLASS_NAME_COLLAPSED, !isOpen)
  247. .attr('aria-expanded', isOpen)
  248. }
  249. }
  250. // Static
  251. static _getTargetFromElement(element) {
  252. const selector = Util.getSelectorFromElement(element)
  253. return selector ? document.querySelector(selector) : null
  254. }
  255. static _jQueryInterface(config) {
  256. return this.each(function () {
  257. const $this = $(this)
  258. let data = $this.data(DATA_KEY)
  259. const _config = {
  260. ...Default,
  261. ...$this.data(),
  262. ...typeof config === 'object' && config ? config : {}
  263. }
  264. if (!data && _config.toggle && typeof config === 'string' && /show|hide/.test(config)) {
  265. _config.toggle = false
  266. }
  267. if (!data) {
  268. data = new Collapse(this, _config)
  269. $this.data(DATA_KEY, data)
  270. }
  271. if (typeof config === 'string') {
  272. if (typeof data[config] === 'undefined') {
  273. throw new TypeError(`No method named "${config}"`)
  274. }
  275. data[config]()
  276. }
  277. })
  278. }
  279. }
  280. /**
  281. * ------------------------------------------------------------------------
  282. * Data Api implementation
  283. * ------------------------------------------------------------------------
  284. */
  285. $(document).on(EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
  286. // preventDefault only for <a> elements (which change the URL) not inside the collapsible element
  287. if (event.currentTarget.tagName === 'A') {
  288. event.preventDefault()
  289. }
  290. const $trigger = $(this)
  291. const selector = Util.getSelectorFromElement(this)
  292. const selectors = [].slice.call(document.querySelectorAll(selector))
  293. $(selectors).each(function () {
  294. const $target = $(this)
  295. const data = $target.data(DATA_KEY)
  296. const config = data ? 'toggle' : $trigger.data()
  297. Collapse._jQueryInterface.call($target, config)
  298. })
  299. })
  300. /**
  301. * ------------------------------------------------------------------------
  302. * jQuery
  303. * ------------------------------------------------------------------------
  304. */
  305. $.fn[NAME] = Collapse._jQueryInterface
  306. $.fn[NAME].Constructor = Collapse
  307. $.fn[NAME].noConflict = () => {
  308. $.fn[NAME] = JQUERY_NO_CONFLICT
  309. return Collapse._jQueryInterface
  310. }
  311. export default Collapse