1/** 2 * Within Viewport 3 * 4 * @description Determines whether an element is completely within the browser viewport 5 * @author Craig Patik, http://patik.com/ 6 * @version 2.0.0 7 * @date 2016-12-19 8 */ 9(function (root, name, factory) { 10 // AMD 11 if (typeof define === 'function' && define.amd) { 12 define([], factory); 13 } 14 // Node and CommonJS-like environments 15 else if (typeof module !== 'undefined' && typeof exports === 'object') { 16 module.exports = factory(); 17 } 18 // Browser global 19 else { 20 root[name] = factory(); 21 } 22}(this, 'withinviewport', function () { 23 var canUseWindowDimensions = window.innerHeight !== undefined; // IE 8 and lower fail this 24 25 /** 26 * Determines whether an element is within the viewport 27 * @param {Object} elem DOM Element (required) 28 * @param {Object} options Optional settings 29 * @return {Boolean} Whether the element was completely within the viewport 30 */ 31 var withinviewport = function withinviewport (elem, options) { 32 var result = false; 33 var metadata = {}; 34 var config = {}; 35 var settings; 36 var isWithin; 37 var isContainerTheWindow; 38 var elemBoundingRect; 39 var containerBoundingRect; 40 var scrollBarWidths = [0, 0]; 41 var sideNamesPattern; 42 var sides; 43 var side; 44 var i; 45 46 // If invoked by the jQuery plugin, get the actual DOM element 47 if (typeof jQuery !== 'undefined' && elem instanceof jQuery) { 48 elem = elem.get(0); 49 } 50 51 if (typeof elem !== 'object' || elem.nodeType !== 1) { 52 throw new Error('First argument must be an element'); 53 } 54 55 // Look for inline settings on the element 56 if (elem.getAttribute('data-withinviewport-settings') && window.JSON) { 57 metadata = JSON.parse(elem.getAttribute('data-withinviewport-settings')); 58 } 59 60 // Settings argument may be a simple string (`top`, `right`, etc) 61 if (typeof options === 'string') { 62 settings = {sides: options}; 63 } 64 else { 65 settings = options || {}; 66 } 67 68 // Build configuration from defaults and user-provided settings and metadata 69 config.container = settings.container || metadata.container || withinviewport.defaults.container || window; 70 config.sides = settings.sides || metadata.sides || withinviewport.defaults.sides || 'all'; 71 config.top = settings.top || metadata.top || withinviewport.defaults.top || 0; 72 config.right = settings.right || metadata.right || withinviewport.defaults.right || 0; 73 config.bottom = settings.bottom || metadata.bottom || withinviewport.defaults.bottom || 0; 74 config.left = settings.left || metadata.left || withinviewport.defaults.left || 0; 75 76 // Extract the DOM node from a jQuery collection 77 if (typeof jQuery !== 'undefined' && config.container instanceof jQuery) { 78 config.container = config.container.get(0); 79 } 80 81 // Use the window as the container if the user specified the body or a non-element 82 if (config.container === document.body || !config.container.nodeType === 1) { 83 config.container = window; 84 } 85 86 isContainerTheWindow = (config.container === window); 87 88 // Element testing methods 89 isWithin = { 90 // Element is below the top edge of the viewport 91 top: function _isWithin_top () { 92 if (isContainerTheWindow) { 93 return (elemBoundingRect.top >= config.top); 94 } 95 else { 96 return (elemBoundingRect.top >= containerScrollTop - (containerScrollTop - containerBoundingRect.top) + config.top); 97 } 98 }, 99 100 // Element is to the left of the right edge of the viewport 101 right: function _isWithin_right () { 102 // Note that `elemBoundingRect.right` is the distance from the *left* of the viewport to the element's far right edge 103 104 if (isContainerTheWindow) { 105 return (elemBoundingRect.right <= (containerBoundingRect.right + containerScrollLeft) - config.right); 106 } 107 else { 108 return (elemBoundingRect.right <= containerBoundingRect.right - scrollBarWidths[0] - config.right); 109 } 110 }, 111 112 // Element is above the bottom edge of the viewport 113 bottom: function _isWithin_bottom () { 114 var containerHeight; 115 116 if (isContainerTheWindow) { 117 if (canUseWindowDimensions) { 118 containerHeight = config.container.innerHe
118ight; 119 } 120 else { 121 containerHeight = document.documentElement.clientHeight; 122 } 123 } 124 else { 125 containerHeight = containerBoundingRect.bottom; 126 } 127 128 // Note that `elemBoundingRect.bottom` is the distance from the *top* of the viewport to the element's bottom edge 129 return (elemBoundingRect.bottom <= containerHeight - scrollBarWidths[1] - config.bottom); 130 }, 131 132 // Element is to the right of the left edge of the viewport 133 left: function _isWithin_left () { 134 if (isContainerTheWindow) { 135 return (elemBoundingRect.left >= config.left); 136 } 137 else { 138 return (elemBoundingRect.left >= containerScrollLeft - (containerScrollLeft - containerBoundingRect.left) + config.left); 139 } 140 }, 141 142 // Element is within all four boundaries 143 all: function _isWithin_all () { 144 // Test each boundary in order of efficiency and likeliness to be false. This way we can avoid running all four functions on most elements. 145 // 1. Top: Quickest to calculate + most likely to be false 146 // 2. Bottom: Note quite as quick to calculate, but also very likely to be false 147 // 3-4. Left and right are both equally unlikely to be false since most sites only scroll vertically, but left is faster 148 return (isWithin.top() && isWithin.bottom() && isWithin.left() && isWithin.right()); 149 } 150 }; 151 152 // Get the element's bounding rectangle with respect to the viewport 153 elemBoundingRect = elem.getBoundingClientRect(); 154 155 // Get viewport dimensions and offsets 156 if (isContainerTheWindow) { 157 containerBoundingRect = document.documentElement.getBoundingClientRect(); 158 containerScrollTop = document.body.scrollTop; 159 containerScrollLeft = document.body.scrollLeft; 160 } 161 else { 162 containerBoundingRect = config.container.getBoundingClientRect(); 163 containerScrollTop = config.container.scrollTop; 164 containerScrollLeft = config.container.scrollLeft; 165 } 166 167 // Don't count the space consumed by scrollbars 168 if (containerScrollLeft) { 169 scrollBarWidths[0] = 18; 170 } 171 172 if (containerScrollTop) { 173 scrollBarWidths[1] = 16; 174 } 175 176 // Test the element against each side of the viewport that was requested 177 sideNamesPattern = /^top$|^right$|^bottom$|^left$|^all$/; 178 // Loop through all of the sides 179 sides = config.sides.split(' '); 180 i = sides.length; 181 while (i--) { 182 side = sides[i].toLowerCase(); 183 184 if (sideNamesPattern.test(side)) { 185 if (isWithin[side]()) { 186 result = true; 187 } 188 else { 189 result = false; 190 191 // Quit as soon as the first failure is found 192 break; 193 } 194 } 195 } 196 197 return result; 198 }; 199 200 // Default settings 201 withinviewport.prototype.defaults = { 202 container: document.body, 203 sides: 'all', 204 top: 0, 205 right: 0, 206 bottom: 0, 207 left: 0 208 }; 209 210 withinviewport.defaults = withinviewport.prototype.defaults; 211 212 /** 213 * Optional enhancements and shortcuts 214 * 215 * @description Uncomment or comment these pieces as they apply to your project and coding preferences 216 */ 217 218 // Shortcut methods for each side of the viewport 219 // Example: `withinviewport.top(elem)` is the same as `withinviewport(elem, 'top')` 220 withinviewport.prototype.top = function _withinviewport_top (element) { 221 return withinviewport(element, 'top'); 222 }; 223 224 withinviewport.prototype.right = function _withinviewport_right (element) { 225 return withinviewport(element, 'right'); 226 }; 227 228 withinviewport.prototype.bottom = function _withinviewport_bottom (element) { 229 return withinviewport(element, 'bottom'); 230 }; 231 232 withinviewport.prototype.left = function _withinviewport_left (element) { 233 return withinviewport(element, 'left'); 234 }; 235 236 return withinviewport; 237}));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.