-
Notifications
You must be signed in to change notification settings - Fork 1
/
sticky-polyfill.js
212 lines (192 loc) · 9.44 KB
/
sticky-polyfill.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
/*!
* sticky-polyfill.js v2.1.0
* (c) 2017-2017 masterkong
* Github: https://github.com/masterkong/sticky-polyfill.git
* Released under the MIT License.
*/
(function(global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.StickyPolyFill = factory());
}(this, (function() {
'use strict';
function polyfill(option) {
var config = {
className: 'sticky-polyfill',
top: '0px',
bottom: '',
zIndex: '99'
};
if (option) {
config.className = option.className || config.className;
if (option.top || !option.bottom) {
config.top = option.top || config.top;
config.bottom = '';
} else {
config.top = '';
config.bottom = option.bottom;
}
config.zIndex = option.zIndex || config.zIndex;
}
console.log('sticky-polyfill:CSS_config',config);
/**
* 参考:https://modernizr.com/download?csspositionsticky-dontmin-setclasses&q=sticky
*/
function ModernizrTestCSSPositionSticky() {
var prefixes = ' -webkit- -moz- -o- -ms- '.split(' ');
var prop = 'position:';
var value = 'sticky';
var el = document.createElement('a');
var mStyle = el.style;
mStyle.cssText = prop + prefixes.join(value + ';' + prop).slice(0, -prop.length);
return mStyle.position.indexOf(value) !== -1;
}
document.addEventListener('DOMContentLoaded', function() {
/**
* sticky-polyfill
*/
var stickyElementsObjectArray = [],isStickyNative = false;
function calcStickyElements(){
var className = config.className;
var stickyElements = Array.prototype.slice.call(document.getElementsByClassName(className));
console.log('sticky-polyfill:Event_scroll:stickyElements',stickyElements);
if(stickyElements.length == 0){
console.error("sticky-polyfill:No Element Found")
return;
}
/**
* sticky-native
*/
//*/
if (ModernizrTestCSSPositionSticky()) {
for (var i = 0, length = stickyElements.length; i < length; i++) {
stickyElements[i].style.position = '-webkit-sticky';
stickyElements[i].style.position = '-moz-sticky';
stickyElements[i].style.position = '-o-sticky';
stickyElements[i].style.position = '-ms-sticky';
stickyElements[i].style.position = 'sticky';
stickyElements[i].style.top = config.top;
stickyElements[i].style.bottom = config.bottom;
stickyElements[i].style.zIndex = config.zIndex;
}
isStickyNative = true;
return;
}
//*/
for (var i = 0, length = stickyElements.length; i < length; i++) {
var stickyEle = stickyElements[i],
clientRect = stickyEle.getBoundingClientRect(),
computedStyle = document.defaultView.getComputedStyle(stickyEle, null);
/**
* 使用opacity在元素上生成一个新的z-index层叠上下文
*/
stickyEle.style.opacity = computedStyle.opacity || '1';
stickyEle.style.zIndex = parseInt(computedStyle.zIndex) || config.zIndex; //z-index默认值为auto
/**
* placeholderEle作为元素的替身有两个作用:
* 1、在position变为fixed时,保证下方的元素不会有突然向上跳的感觉
* 2、在滚动时正确计算元素何时恢复原来的position
* 参考:https://www.sitepoint.com/css-position-sticky-introduction-polyfills/
*/
var placeholderEle = document.createElement('div');
placeholderEle.style.width = clientRect.width + 'px';
placeholderEle.style.height = clientRect.height + 'px';
placeholderEle.style.margin = computedStyle.margin;
stickyElementsObjectArray.push({
stickyEle: stickyEle,
config: config,
cssText: stickyEle.style.cssText,
clientRect: clientRect,
placeholderEle: placeholderEle,
isPlaceholderEleAdded: false
});
}
}
//OnScroll(); //先运行一次,保证以bottom确定sticky的时候正常
window.addEventListener('scroll', OnScroll);
function OnScroll() {
if(isStickyNative){
return;
}
/**
* sticky-polyfill元素有可能在"DOMContentLoaded"发生时还未生成好,比如在Vue等框架使用时
*/
if(stickyElementsObjectArray.length==0){
calcStickyElements();
}
/**
* 在Safari中测试发现clientHeight会有变化,改为实时获取clientHeight
*/
var clientHeight = window.innerHeight;
if (typeof clientHeight != "number") {
clientHeight = document.documentElement.clientHeight;
}
for (var i = 0, length = stickyElementsObjectArray.length; i < length; i++) {
stickyExe(stickyElementsObjectArray[i]);
}
function stickyExe(stickyObject) {
var stickyEle = stickyObject.stickyEle,
clientRect = stickyObject.clientRect,
placeholderEle = stickyObject.placeholderEle,
config = stickyObject.config,
cssText = stickyObject.cssText;
/**
* 再次说明,如果有placeholderEle替身元素,计算滚动时以placeholderEle为准.
* 因为此时原来元素的position为fixed,top/bottom始终不变
*/
var pos = stickyEle.getBoundingClientRect();
stickyObject.isPlaceholderEleAdded ? pos = placeholderEle.getBoundingClientRect() : '';
if (config.top) {
/**
*以top确定sticky的距离
*
*/
var stickyTop = parseFloat(config.top) || 0;
if (pos.top <= stickyTop && !stickyObject.isPlaceholderEleAdded) {
stickyEle.style.marginTop = '0px';
//stickyEle.style.top = config.top;
stickyEle.style.top = stickyTop + 'px';
stickyEle.style.zIndex = config.zIndex;
stickyFun();
} else if (pos.top > stickyTop && stickyObject.isPlaceholderEleAdded) {
restoreFun();
}
} else {
/**
* 以bottom确定sticky的距离
*/
var stickyBottom = parseFloat(config.bottom) || 0;
if (clientHeight - pos.bottom <= stickyBottom && !stickyObject.isPlaceholderEleAdded) {
stickyEle.style.marginBottom = '0px';
//stickyEle.style.bottom = config.bottom;
stickyEle.style.bottom = stickyBottom + 'px';
stickyEle.style.zIndex = config.zIndex;
stickyFun();
} else if (clientHeight - pos.bottom > stickyBottom && stickyObject.isPlaceholderEleAdded) {
restoreFun();
}
}
function stickyFun() {
//在原来元素下方生成一个替身元素
stickyEle.parentNode.insertBefore(placeholderEle, stickyEle);
stickyObject.isPlaceholderEleAdded = true;
stickyEle.style.position = 'fixed';
stickyEle.style.left = clientRect.left + 'px';
stickyEle.style.width = clientRect.width + 'px';
stickyEle.style.marginLeft = '0px';
stickyEle.style.marginRight = '0px';
stickyEle.style.boxSizing = 'border-box'; //clientRect.width是以border-box模型计算的
}
function restoreFun() {
stickyEle.parentNode.removeChild(placeholderEle);
stickyEle.style.cssText = cssText;
stickyObject.isPlaceholderEleAdded = false;
}
}
}
}, false)
}
return {
polyfill: polyfill
}
})));