]>
git.ipfire.org Git - ipfire.org.git/blob - src/scss/bootstrap-4.0.0-alpha.6/js/src/scrollspy.js
1 import Util
from './util'
5 * --------------------------------------------------------------------------
6 * Bootstrap (v4.0.0-alpha.6): scrollspy.js
7 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
8 * --------------------------------------------------------------------------
11 const ScrollSpy
= (($) => {
15 * ------------------------------------------------------------------------
17 * ------------------------------------------------------------------------
20 const NAME
= 'scrollspy'
21 const VERSION
= '4.0.0-alpha.6'
22 const DATA_KEY
= 'bs.scrollspy'
23 const EVENT_KEY
= `.${DATA_KEY}`
24 const DATA_API_KEY
= '.data-api'
25 const JQUERY_NO_CONFLICT
= $.fn
[NAME
]
36 target
: '(string|element)'
40 ACTIVATE
: `activate${EVENT_KEY}`,
41 SCROLL
: `scroll${EVENT_KEY}`,
42 LOAD_DATA_API
: `load${EVENT_KEY}${DATA_API_KEY}`
46 DROPDOWN_ITEM
: 'dropdown-item',
47 DROPDOWN_MENU
: 'dropdown-menu',
48 NAV_LINK
: 'nav-link',
54 DATA_SPY
: '[data-spy="scroll"]',
56 LIST_ITEM
: '.list-item',
58 LI_DROPDOWN
: 'li.dropdown',
59 NAV_LINKS
: '.nav-link',
60 DROPDOWN
: '.dropdown',
61 DROPDOWN_ITEMS
: '.dropdown-item',
62 DROPDOWN_TOGGLE
: '.dropdown-toggle'
65 const OffsetMethod
= {
72 * ------------------------------------------------------------------------
74 * ------------------------------------------------------------------------
79 constructor(element
, config
) {
80 this._element
= element
81 this._scrollElement
= element
.tagName
=== 'BODY' ? window
: element
82 this._config
= this._getConfig(config
)
83 this._selector
= `${this._config.target} ${Selector.NAV_LINKS},`
84 + `${this._config.target} ${Selector.DROPDOWN_ITEMS}`
87 this._activeTarget
= null
88 this._scrollHeight
= 0
90 $(this._scrollElement
).on(Event
.SCROLL
, (event
) => this._process(event
))
99 static get VERSION() {
103 static get Default() {
111 const autoMethod
= this._scrollElement
!== this._scrollElement
.window
?
112 OffsetMethod
.POSITION
: OffsetMethod
.OFFSET
114 const offsetMethod
= this._config
.method
=== 'auto' ?
115 autoMethod
: this._config
.method
117 const offsetBase
= offsetMethod
=== OffsetMethod
.POSITION
?
118 this._getScrollTop() : 0
123 this._scrollHeight
= this._getScrollHeight()
125 const targets
= $.makeArray($(this._selector
))
130 const targetSelector
= Util
.getSelectorFromElement(element
)
132 if (targetSelector
) {
133 target
= $(targetSelector
)[0]
136 if (target
&& (target
.offsetWidth
|| target
.offsetHeight
)) {
137 // todo (fat): remove sketch reliance on jQuery position/offset
139 $(target
)[offsetMethod
]().top
+ offsetBase
,
145 .filter((item
) => item
)
146 .sort((a
, b
) => a
[0] - b
[0])
148 this._offsets
.push(item
[0])
149 this._targets
.push(item
[1])
154 $.removeData(this._element
, DATA_KEY
)
155 $(this._scrollElement
).off(EVENT_KEY
)
158 this._scrollElement
= null
160 this._selector
= null
163 this._activeTarget
= null
164 this._scrollHeight
= null
171 config
= $.extend({}, Default
, config
)
173 if (typeof config
.target
!== 'string') {
174 let id
= $(config
.target
).attr('id')
176 id
= Util
.getUID(NAME
)
177 $(config
.target
).attr('id', id
)
179 config
.target
= `#${id}`
182 Util
.typeCheckConfig(NAME
, config
, DefaultType
)
188 return this._scrollElement
=== window
?
189 this._scrollElement
.pageYOffset
: this._scrollElement
.scrollTop
193 return this._scrollElement
.scrollHeight
|| Math
.max(
194 document
.body
.scrollHeight
,
195 document
.documentElement
.scrollHeight
200 return this._scrollElement
=== window
?
201 window
.innerHeight
: this._scrollElement
.offsetHeight
205 const scrollTop
= this._getScrollTop() + this._config
.offset
206 const scrollHeight
= this._getScrollHeight()
207 const maxScroll
= this._config
.offset
209 - this._getOffsetHeight()
211 if (this._scrollHeight
!== scrollHeight
) {
215 if (scrollTop
>= maxScroll
) {
216 const target
= this._targets
[this._targets
.length
- 1]
218 if (this._activeTarget
!== target
) {
219 this._activate(target
)
224 if (this._activeTarget
&& scrollTop
< this._offsets
[0] && this._offsets
[0] > 0) {
225 this._activeTarget
= null
230 for (let i
= this._offsets
.length
; i
--;) {
231 const isActiveTarget
= this._activeTarget
!== this._targets
[i
]
232 && scrollTop
>= this._offsets
[i
]
233 && (this._offsets
[i
+ 1] === undefined ||
234 scrollTop
< this._offsets
[i
+ 1])
236 if (isActiveTarget
) {
237 this._activate(this._targets
[i
])
243 this._activeTarget
= target
247 let queries
= this._selector
.split(',')
248 queries
= queries
.map((selector
) => {
249 return `${selector}[data-target="${target}"],` +
250 `${selector}[href="${target}"]`
253 const $link
= $(queries
.join(','))
255 if ($link
.hasClass(ClassName
.DROPDOWN_ITEM
)) {
256 $link
.closest(Selector
.DROPDOWN
).find(Selector
.DROPDOWN_TOGGLE
).addClass(ClassName
.ACTIVE
)
257 $link
.addClass(ClassName
.ACTIVE
)
259 // todo (fat) this is kinda sus...
260 // recursively add actives to tested nav-links
261 $link
.parents(Selector
.LI
).find(`> ${Selector.NAV_LINKS}`).addClass(ClassName
.ACTIVE
)
264 $(this._scrollElement
).trigger(Event
.ACTIVATE
, {
265 relatedTarget
: target
270 $(this._selector
).filter(Selector
.ACTIVE
).removeClass(ClassName
.ACTIVE
)
276 static _jQueryInterface(config
) {
277 return this.each(function () {
278 let data
= $(this).data(DATA_KEY
)
279 const _config
= typeof config
=== 'object' && config
282 data
= new ScrollSpy(this, _config
)
283 $(this).data(DATA_KEY
, data
)
286 if (typeof config
=== 'string') {
287 if (data
[config
] === undefined) {
288 throw new Error(`No method named "${config}"`)
300 * ------------------------------------------------------------------------
301 * Data Api implementation
302 * ------------------------------------------------------------------------
305 $(window
).on(Event
.LOAD_DATA_API
, () => {
306 const scrollSpys
= $.makeArray($(Selector
.DATA_SPY
))
308 for (let i
= scrollSpys
.length
; i
--;) {
309 const $spy
= $(scrollSpys
[i
])
310 ScrollSpy
._jQueryInterface
.call($spy
, $spy
.data())
316 * ------------------------------------------------------------------------
318 * ------------------------------------------------------------------------
321 $.fn
[NAME
] = ScrollSpy
._jQueryInterface
322 $.fn
[NAME
].Constructor
= ScrollSpy
323 $.fn
[NAME
].noConflict = function () {
324 $.fn
[NAME
] = JQUERY_NO_CONFLICT
325 return ScrollSpy
._jQueryInterface
332 export default ScrollSpy