blinker / firefox.plugin / data / withinViewport.js @ a03cd52e
History | View | Annotate | Download (7.634 KB)
1 | a03cd52e | Thies Pfeiffer | /**
|
---|---|---|---|
2 | * Within Viewport
|
||
3 | *
|
||
4 | * @description Determines whether an element is completely
|
||
5 | * within the browser viewport
|
||
6 | * @author Craig Patik, http://patik.com/
|
||
7 | * @version 0.0.4
|
||
8 | * @date 2014-07-05
|
||
9 | */
|
||
10 | ;(function() {
|
||
11 | /**
|
||
12 | * Determines whether an element is within the viewport
|
||
13 | * @param {Object} elem DOM Element (required)
|
||
14 | * @param {Object} options Optional settings
|
||
15 | * @return {Boolean} Whether the element was completely within the viewport
|
||
16 | */
|
||
17 | var withinViewport = function _withinViewport(elem, options) { |
||
18 | var result = false, |
||
19 | metadata = {}, |
||
20 | config = {}, |
||
21 | settings, useHtmlElem, isWithin, scrollOffset, elemOffset, arr, i, side; |
||
22 | |||
23 | if (typeof jQuery !== 'undefined' && elem instanceof jQuery) { |
||
24 | elem = elem.get(0);
|
||
25 | } |
||
26 | |||
27 | if (typeof elem !== 'object' || elem.nodeType !== 1) { |
||
28 | throw new Error('First argument must be an element'); |
||
29 | } |
||
30 | |||
31 | if (elem.getAttribute('data-withinviewport-settings') && window.JSON) { |
||
32 | metadata = JSON.parse(elem.getAttribute('data-withinviewport-settings'));
|
||
33 | } |
||
34 | |||
35 | // Settings argument may be a simple string (`top`, `right`, etc)
|
||
36 | if (typeof options === 'string') { |
||
37 | settings = {sides: options};
|
||
38 | } |
||
39 | else {
|
||
40 | settings = options || {}; |
||
41 | } |
||
42 | |||
43 | // Build configuration from defaults and given settings
|
||
44 | config.container = settings.container || metadata.container || withinViewport.defaults.container || document.body; |
||
45 | config.sides = settings.sides || metadata.sides || withinViewport.defaults.sides || 'all';
|
||
46 | config.top = settings.top || metadata.top || withinViewport.defaults.top || 0;
|
||
47 | config.right = settings.right || metadata.right || withinViewport.defaults.right || 0;
|
||
48 | config.bottom = settings.bottom || metadata.bottom || withinViewport.defaults.bottom || 0;
|
||
49 | config.left = settings.left || metadata.left || withinViewport.defaults.left || 0;
|
||
50 | |||
51 | // Whether we can use the `<html`> element for `scrollTop`
|
||
52 | // Unfortunately at the moment I can't find a way to do this without UA-sniffing
|
||
53 | useHtmlElem = !/Chrome/.test(navigator.userAgent);
|
||
54 | |||
55 | // Element testing methods
|
||
56 | isWithin = { |
||
57 | // Element is below the top edge of the viewport
|
||
58 | top: function _isWithin_top() { |
||
59 | return elemOffset[1] >= scrollOffset[1] + config.top; |
||
60 | }, |
||
61 | |||
62 | // Element is to the left of the right edge of the viewport
|
||
63 | right: function _isWithin_right() { |
||
64 | var container = (config.container === document.body) ? window : config.container;
|
||
65 | |||
66 | return elemOffset[0] + elem.offsetWidth <= container.innerWidth + scrollOffset[0] - config.right; |
||
67 | }, |
||
68 | |||
69 | // Element is above the bottom edge of the viewport
|
||
70 | bottom: function _isWithin_bottom() { |
||
71 | var container = (config.container === document.body) ? window : config.container;
|
||
72 | |||
73 | return elemOffset[1] + elem.offsetHeight <= scrollOffset[1] + container.innerHeight - config.bottom; |
||
74 | }, |
||
75 | |||
76 | // Element is to the right of the left edge of the viewport
|
||
77 | left: function _isWithin_left() { |
||
78 | return elemOffset[0] >= scrollOffset[0] + config.left; |
||
79 | }, |
||
80 | |||
81 | all: function _isWithin_all() { |
||
82 | return (isWithin.top() && isWithin.right() && isWithin.bottom() && isWithin.left());
|
||
83 | } |
||
84 | }; |
||
85 | |||
86 | // Current offset values
|
||
87 | scrollOffset = (function _scrollOffset() { |
||
88 | var x = config.container.scrollLeft,
|
||
89 | y = config.container.scrollTop; |
||
90 | |||
91 | if (y === 0) { |
||
92 | if (config.container.pageYOffset) {
|
||
93 | y = config.container.pageYOffset; |
||
94 | } |
||
95 | else if (window.pageYOffset) { |
||
96 | y = window.pageYOffset; |
||
97 | } |
||
98 | else {
|
||
99 | if (config.container === document.body) {
|
||
100 | if (useHtmlElem) {
|
||
101 | y = (config.container.parentElement) ? config.container.parentElement.scrollTop : 0;
|
||
102 | } |
||
103 | else {
|
||
104 | y = (config.container.parentElement) ? config.container.parentElement.scrollTop : 0;
|
||
105 | } |
||
106 | } |
||
107 | else {
|
||
108 | y = (config.container.parentElement) ? config.container.parentElement.scrollTop : 0;
|
||
109 | } |
||
110 | } |
||
111 | } |
||
112 | |||
113 | if (x === 0) { |
||
114 | if (config.container.pageXOffset) {
|
||
115 | x = config.container.pageXOffset; |
||
116 | } |
||
117 | else if (window.pageXOffset) { |
||
118 | x = window.pageXOffset; |
||
119 | } |
||
120 | else {
|
||
121 | if (config.container === document.body) {
|
||
122 | x = (config.container.parentElement) ? config.container.parentElement.scrollLeft : 0;
|
||
123 | } |
||
124 | else {
|
||
125 | x = (config.container.parentElement) ? config.container.parentElement.scrollLeft : 0;
|
||
126 | } |
||
127 | } |
||
128 | } |
||
129 | |||
130 | return [x, y];
|
||
131 | }()); |
||
132 | |||
133 | elemOffset = (function _elemOffset() { |
||
134 | var el = elem,
|
||
135 | x = 0,
|
||
136 | y = 0;
|
||
137 | |||
138 | if (el.parentNode) {
|
||
139 | x = el.offsetLeft; |
||
140 | y = el.offsetTop; |
||
141 | |||
142 | el = el.parentNode; |
||
143 | while (el) {
|
||
144 | if (el == config.container) {
|
||
145 | break;
|
||
146 | } |
||
147 | |||
148 | x += el.offsetLeft; |
||
149 | y += el.offsetTop; |
||
150 | |||
151 | el = el.parentNode; |
||
152 | } |
||
153 | } |
||
154 | |||
155 | return [x, y];
|
||
156 | })(); |
||
157 | |||
158 | // Test the element against each side of the viewport that was requested
|
||
159 | arr = config.sides.split(' ');
|
||
160 | i = arr.length; |
||
161 | while (i--) {
|
||
162 | side = arr[i].toLowerCase(); |
||
163 | if (/top|right|bottom|left|all/.test(side)) { |
||
164 | if (isWithin[side]()) {
|
||
165 | result = true;
|
||
166 | } |
||
167 | else {
|
||
168 | result = false;
|
||
169 | // Quit as soon as the first failure is found
|
||
170 | break;
|
||
171 | } |
||
172 | } |
||
173 | } |
||
174 | |||
175 | return result;
|
||
176 | }; // end of `withinViewport()`
|
||
177 | |||
178 | // Default settings
|
||
179 | withinViewport.prototype.defaults = { |
||
180 | container: document.body,
|
||
181 | sides: 'all', |
||
182 | top: 0, |
||
183 | right: 0, |
||
184 | bottom: 0, |
||
185 | left: 0 |
||
186 | }; |
||
187 | |||
188 | withinViewport.defaults = withinViewport.prototype.defaults; |
||
189 | |||
190 | // Make function available globally
|
||
191 | window.withinViewport = withinViewport; |
||
192 | |||
193 | /**
|
||
194 | * Optional enhancements and shortcuts
|
||
195 | *
|
||
196 | * @description Uncomment or comment these pieces as they apply to your project and coding preferences
|
||
197 | */
|
||
198 | |||
199 | // Shortcut methods for each side of the viewport
|
||
200 | // Ex: withinViewport.top(elem) is the same as withinViewport(elem, 'top')
|
||
201 | withinViewport.prototype.top = function _withinViewport_top(element) { |
||
202 | return withinViewport(element, 'top'); |
||
203 | }; |
||
204 | |||
205 | withinViewport.prototype.right = function _withinViewport_right(element) { |
||
206 | return withinViewport(element, 'right'); |
||
207 | }; |
||
208 | |||
209 | withinViewport.prototype.bottom = function _withinViewport_bottom(element) { |
||
210 | return withinViewport(element, 'bottom'); |
||
211 | }; |
||
212 | |||
213 | withinViewport.prototype.left = function _withinViewport_left(element) { |
||
214 | return withinViewport(element, 'left'); |
||
215 | }; |
||
216 | }()); |