forked from jonathantneal/angular-sticky
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathangular-sticky.js
More file actions
153 lines (122 loc) · 3.88 KB
/
angular-sticky.js
File metadata and controls
153 lines (122 loc) · 3.88 KB
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
(function (namespace) {
// set sticky module and directive
angular.module(namespace, []).directive(namespace, function () {
return {
link: function (scope, angularElement, attrs) {
var
// get element
element = angularElement[0],
// get document
document = element.ownerDocument,
// get window
window = document.defaultView,
// get wrapper
wrapper = document.createElement('span'),
// cache style
style = element.getAttribute('style'),
// get options
bottom = parseFloat(attrs[namespace + 'Bottom']),
media = window.matchMedia(attrs[namespace + 'Media'] || 'all'),
top = parseFloat(attrs[namespace + 'Top']),
stickyClass = attrs[namespace + 'Class'],
// initialize states
activeBottom = false,
activeTop = false,
offset = {};
// configure wrapper
wrapper.className = 'is-' + namespace;
// activate sticky
function activate() {
// get element computed style
var
computedStyle = getComputedStyle(element),
position = activeTop ? 'top:' + top : 'bottom:' + bottom,
parentNode = element.parentNode,
nextSibling = element.nextSibling;
// replace element with wrapper containing element
wrapper.appendChild(element);
if (parentNode) {
parentNode.insertBefore(wrapper, nextSibling);
}
// style wrapper
wrapper.setAttribute(
'style',
'display:' + computedStyle.display + ';' +
'height:' + element.offsetHeight + 'px;' +
'margin:' + computedStyle.margin + ';' +
'width:' + element.offsetWidth + 'px;' +
'clear:' + computedStyle.clear + ';'
);
// style element
element.setAttribute('style', 'left:' + offset.left + 'px;margin:0;position:fixed;transition:none;' + position + 'px;width:' + computedStyle.width);
element.classList.add(stickyClass);
}
// deactivate sticky
function deactivate() {
var
parentNode = wrapper.parentNode,
nextSibling = wrapper.nextSibling;
// replace wrapper with element
parentNode.removeChild(wrapper);
parentNode.insertBefore(element, nextSibling);
// unstyle element
if (style === null) {
element.removeAttribute('style');
} else {
element.setAttribute('style', style);
}
element.classList.remove(stickyClass);
// unstyle wrapper
wrapper.removeAttribute('style');
activeTop = activeBottom = false;
}
// window scroll listener
function onscroll() {
// if activated
if (activeTop || activeBottom) {
// get wrapper offset
offset = wrapper.getBoundingClientRect();
activeBottom = !isNaN(bottom) && offset.top > window.innerHeight - bottom - wrapper.offsetHeight;
activeTop = !isNaN(top) && offset.top < top;
// deactivate if wrapper is inside range
if (!activeTop && !activeBottom) {
deactivate();
}
}
// if not activated
else if (media.matches) {
// get element offset
offset = element.getBoundingClientRect();
activeBottom = !isNaN(bottom) && offset.top > window.innerHeight - bottom - element.offsetHeight;
activeTop = !isNaN(top) && offset.top < top;
// activate if element is outside range
if (activeTop || activeBottom) {
activate();
}
}
}
// window resize listener
function onresize() {
// conditionally deactivate sticky
if (activeTop || activeBottom) {
deactivate();
}
// re-initialize sticky
onscroll();
}
// destroy listener
function ondestroy() {
onresize();
window.removeEventListener('scroll', onscroll);
window.removeEventListener('resize', onresize);
}
// bind listeners
window.addEventListener('scroll', onscroll);
window.addEventListener('resize', onresize);
scope.$on('$destroy', ondestroy);
// initialize sticky
onscroll();
}
};
});
})('sticky');