Statistics
| Branch: | Revision:

blinker / firefox.plugin / data / withinViewport.js @ e75d6b65

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
}());