|
1 | 1 | /* global CONFIG */ |
2 | 2 |
|
3 | | -($ => { |
4 | | - class Affix { |
5 | | - constructor(element, options) { |
6 | | - this.options = $.extend({ |
7 | | - offset: 0, |
8 | | - target: window |
9 | | - }, options); |
10 | | - this.$target = $(this.options.target) |
11 | | - .on('scroll.bs.affix.data-api', this.checkPosition.bind(this)) |
12 | | - .on('click.bs.affix.data-api', this.checkPositionWithEventLoop.bind(this)); |
13 | | - this.$element = $(element); |
14 | | - this.affixed = null; |
15 | | - this.unpin = null; |
16 | | - this.pinnedOffset = null; |
17 | | - this.checkPosition(); |
18 | | - } |
19 | | - getState(scrollHeight, height, offsetTop, offsetBottom) { |
20 | | - let scrollTop = this.$target.scrollTop(); |
21 | | - let position = this.$element.offset(); |
22 | | - let targetHeight = this.$target.height(); |
23 | | - if (offsetTop != null && this.affixed === 'top') return scrollTop < offsetTop ? 'top' : false; |
24 | | - if (this.affixed === 'bottom') { |
25 | | - if (offsetTop != null) return scrollTop + this.unpin <= position.top ? false : 'bottom'; |
26 | | - return scrollTop + targetHeight <= scrollHeight - offsetBottom ? false : 'bottom'; |
27 | | - } |
28 | | - let initializing = this.affixed == null; |
29 | | - let colliderTop = initializing ? scrollTop : position.top; |
30 | | - let colliderHeight = initializing ? targetHeight : height; |
31 | | - if (offsetTop != null && scrollTop <= offsetTop) return 'top'; |
32 | | - if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'; |
33 | | - return false; |
34 | | - } |
35 | | - getPinnedOffset() { |
36 | | - if (this.pinnedOffset) return this.pinnedOffset; |
37 | | - this.$element.removeClass(Affix.RESET).addClass('affix'); |
38 | | - let scrollTop = this.$target.scrollTop(); |
39 | | - let position = this.$element.offset(); |
40 | | - return (this.pinnedOffset = position.top - scrollTop); |
| 3 | +var Affix = { |
| 4 | + init: function(element, options) { |
| 5 | + this.options = Object.assign({ |
| 6 | + offset: 0, |
| 7 | + target: window |
| 8 | + }, options); |
| 9 | + this.target = this.options.target; |
| 10 | + this.target.addEventListener('scroll', this.checkPosition.bind(this)); |
| 11 | + window.matchMedia('(min-width: 992px)').addListener(event => { |
| 12 | + if (event.matches) this.checkPosition(); |
| 13 | + }); |
| 14 | + this.element = element; |
| 15 | + this.affixed = null; |
| 16 | + this.unpin = null; |
| 17 | + this.pinnedOffset = null; |
| 18 | + this.checkPosition(); |
| 19 | + }, |
| 20 | + getState: function(scrollHeight, height, offsetTop, offsetBottom) { |
| 21 | + let scrollTop = this.target.scrollY; |
| 22 | + let targetHeight = this.target.innerHeight; |
| 23 | + if (offsetTop != null && this.affixed === 'top') return scrollTop < offsetTop ? 'top' : false; |
| 24 | + if (this.affixed === 'bottom') { |
| 25 | + if (offsetTop != null) return scrollTop + this.unpin <= $(this.element).offset().top ? false : 'bottom'; |
| 26 | + return scrollTop + targetHeight <= scrollHeight - offsetBottom ? false : 'bottom'; |
41 | 27 | } |
42 | | - checkPositionWithEventLoop() { |
43 | | - setTimeout(this.checkPosition.bind(this), 1); |
| 28 | + let initializing = this.affixed === null; |
| 29 | + let colliderTop = initializing ? scrollTop : $(this.element).offset().top; |
| 30 | + let colliderHeight = initializing ? targetHeight : height; |
| 31 | + if (offsetTop != null && scrollTop <= offsetTop) return 'top'; |
| 32 | + if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'; |
| 33 | + return false; |
| 34 | + }, |
| 35 | + getPinnedOffset: function() { |
| 36 | + if (this.pinnedOffset) return this.pinnedOffset; |
| 37 | + this.element.classList.remove('affix-top', 'affix-bottom'); |
| 38 | + this.element.classList.add('affix'); |
| 39 | + let scrollTop = this.target.scrollY; |
| 40 | + return (this.pinnedOffset = $(this.element).offset().top - scrollTop); |
| 41 | + }, |
| 42 | + checkPosition: function() { |
| 43 | + if (window.getComputedStyle(this.element).display === 'none') return; |
| 44 | + let height = $(this.element).height(); |
| 45 | + let offset = this.options.offset; |
| 46 | + let offsetTop = offset.top; |
| 47 | + let offsetBottom = offset.bottom; |
| 48 | + let scrollHeight = document.body.scrollHeight; |
| 49 | + let affix = this.getState(scrollHeight, height, offsetTop, offsetBottom); |
| 50 | + if (this.affixed !== affix) { |
| 51 | + if (this.unpin != null) this.element.style.top = ''; |
| 52 | + let affixType = 'affix' + (affix ? '-' + affix : ''); |
| 53 | + this.affixed = affix; |
| 54 | + this.unpin = affix === 'bottom' ? this.getPinnedOffset() : null; |
| 55 | + this.element.classList.remove('affix', 'affix-top', 'affix-bottom'); |
| 56 | + this.element.classList.add(affixType); |
44 | 57 | } |
45 | | - checkPosition() { |
46 | | - if (!this.$element.is(':visible')) return; |
47 | | - let height = this.$element.height(); |
48 | | - let offset = this.options.offset; |
49 | | - let offsetTop = offset.top; |
50 | | - let offsetBottom = offset.bottom; |
51 | | - let scrollHeight = Math.max($(document).height(), $(document.body).height()); |
52 | | - if (typeof offset !== 'object') offsetBottom = offsetTop = offset; |
53 | | - if (typeof offsetTop === 'function') offsetTop = offset.top(this.$element); |
54 | | - if (typeof offsetBottom === 'function') offsetBottom = offset.bottom(this.$element); |
55 | | - let affix = this.getState(scrollHeight, height, offsetTop, offsetBottom); |
56 | | - if (this.affixed !== affix) { |
57 | | - if (this.unpin != null) this.$element.css('top', ''); |
58 | | - let affixType = 'affix' + (affix ? '-' + affix : ''); |
59 | | - let e = new $.Event(affixType + '.bs.affix'); |
60 | | - this.$element.trigger(e); |
61 | | - if (e.isDefaultPrevented()) return; |
62 | | - this.affixed = affix; |
63 | | - this.unpin = affix === 'bottom' ? this.getPinnedOffset() : null; |
64 | | - this.$element |
65 | | - .removeClass(Affix.RESET) |
66 | | - .addClass(affixType) |
67 | | - .trigger(affixType.replace('affix', 'affixed') + '.bs.affix'); |
68 | | - } |
69 | | - if (affix === 'bottom') { |
70 | | - this.$element.offset({ |
71 | | - top: scrollHeight - height - offsetBottom |
72 | | - }); |
73 | | - } |
| 58 | + if (affix === 'bottom') { |
| 59 | + $(this.element).offset({ |
| 60 | + top: scrollHeight - height - offsetBottom |
| 61 | + }); |
74 | 62 | } |
75 | 63 | } |
76 | | - |
77 | | - Affix.RESET = 'affix affix-top affix-bottom'; |
78 | | - |
79 | | - function Plugin(option) { |
80 | | - return this.each(function() { |
81 | | - let $this = $(this); |
82 | | - let data = $this.data('bs.affix'); |
83 | | - let options = typeof option === 'object' && option; |
84 | | - if (!data) $this.data('bs.affix', data = new Affix(this, options)); |
85 | | - if (typeof option === 'string') data[option](); |
86 | | - }); |
87 | | - } |
88 | | - |
89 | | - $.fn.affix = Plugin; |
90 | | - $.fn.affix.Constructor = Affix; |
91 | | -})(jQuery); |
| 64 | +}; |
92 | 65 |
|
93 | 66 | window.addEventListener('DOMContentLoaded', () => { |
94 | 67 | const sidebarOffset = CONFIG.sidebar.offset || 12; |
95 | | - const sidebarInner = document.querySelector('.sidebar-inner'); |
96 | | - |
97 | | - const getHeaderOffset = () => document.querySelector('.header-inner').offsetHeight + sidebarOffset; |
98 | 68 |
|
99 | | - const getFooterOffset = () => { |
100 | | - let footer = document.querySelector('#footer'); |
101 | | - let footerInner = document.querySelector('.footer-inner'); |
102 | | - let footerMargin = footer.offsetHeight - footerInner.offsetHeight; |
103 | | - let footerOffset = footer.offsetHeight + footerMargin; |
104 | | - return footerOffset; |
105 | | - }; |
| 69 | + let headerOffset = document.querySelector('.header-inner').offsetHeight + sidebarOffset; |
| 70 | + let footer = document.querySelector('#footer'); |
| 71 | + let footerInner = document.querySelector('.footer-inner'); |
| 72 | + let footerMargin = footer.offsetHeight - footerInner.offsetHeight; |
| 73 | + let footerOffset = footer.offsetHeight + footerMargin; |
106 | 74 |
|
107 | | - const initAffix = () => { |
108 | | - let headerOffset = getHeaderOffset(); |
109 | | - let footerOffset = getFooterOffset(); |
110 | | - $('.sidebar-inner').affix({ |
111 | | - offset: { |
112 | | - top : headerOffset - sidebarOffset, |
113 | | - bottom: footerOffset |
114 | | - } |
115 | | - }); |
116 | | - document.querySelector('#sidebar').css({ |
117 | | - 'margin-top' : `${headerOffset}px`, |
118 | | - 'margin-left': 'auto' |
119 | | - }); |
120 | | - }; |
121 | | - |
122 | | - window.matchMedia('(min-width: 992px)').addListener(event => { |
123 | | - if (event.matches) { |
124 | | - $(window).off('.affix'); |
125 | | - $('.sidebar-inner').removeData('bs.affix'); |
126 | | - sidebarInner.classList.remove('affix', 'affix-top', 'affix-bottom'); |
127 | | - initAffix(); |
| 75 | + Affix.init(document.querySelector('.sidebar-inner'), { |
| 76 | + offset: { |
| 77 | + top : headerOffset - sidebarOffset, |
| 78 | + bottom: footerOffset |
128 | 79 | } |
129 | 80 | }); |
130 | | - |
131 | | - initAffix(); |
| 81 | + document.querySelector('.sidebar').css({ |
| 82 | + 'margin-top' : `${headerOffset}px`, |
| 83 | + 'margin-left': 'auto' |
| 84 | + }); |
132 | 85 | }); |
0 commit comments