;(function (root) { function throttle (fn, threshhold, scope) { threshhold || (threshhold = 100) var last, deferTimer return function () { var context = scope || this var now = +(new Date()) var args = arguments if (last && now < last + threshhold) { clearTimeout(deferTimer) deferTimer = setTimeout(function () { last = now fn.apply(context, args) }, threshhold) } else { last = now fn.apply(context, args) } } } function hasClass (el, name) { return new RegExp(' ' + name + ' ').test(' ' + el.className + ' ') } function addClass (el, name) { if (!hasClass(el, name)) { el.className += ' ' + name } return el } function removeClass (el, name) { var newClass = ' ' + el.className.replace(/[\t\r\n]/g, ' ') + ' ' if (hasClass(el, name)) { while (newClass.indexOf(' ' + name + ' ') >= 0) { newClass = newClass.replace(' ' + name + ' ', ' ') } el.className = newClass.replace(/^\s+|\s+$/g, '') } return el } function removeEvent (el, name, fn) { if (el.removeEventListener) { return el.removeEventListener(name, fn) } else if (el.detachEvent) { return el.detachEvent('on' + name, fn) } } function addEvent (el, name, fn) { if (el.addEventListener) { return el.addEventListener(name, fn, false) } else if (el.attachEvent) { return el.attachEvent('on' + name, fn) } } function getScrollTop () { if (typeof window.pageYOffset !== 'undefined') { return window.pageYOffset } else { var b = document.body var d = document.documentElement d = d.clientHeight ? d : b return d.scrollTop } } function isInView (obj) { var winTop = getScrollTop() var winBottom = winTop + window.innerHeight var objTop = obj.getBoundingClientRect().top + window.scrollY var objBottom = objTop + obj.offsetHeight var offset = 0 if ((objTop <= winBottom + offset) && (objBottom >= winTop)) { return true } return false } /** * @desc Create an InView instance. * * @class * @func InView * @param {HTMLElement} element - element to detect when scrolled to view * @param {scrollCallback} scrollCallback - callback function fired on scroll event * @return {HTMLElement} - element * * @example * var el = document.querySelector('.item'); * * var InView = InView(el, function(isInView, data) { * if (isInView) { * console.log('in view'); * } else { * if (data.windowScrollTop - (data.elementOffsetTop - data.inViewHeight) > data.inViewHeight) { * console.log('not in view (scroll up)'); * } else { * console.log('not in view (scroll down)'); * } * } * }); */ function InView (el, callback) { var _this = this if (!(_this instanceof InView)) { return new InView(el, callback) } _this.el = el _this.callback = callback.bind(_this) _this.destroy = function () {} if (!el) { return _this } var isDestroyed = false var check = function check (e) { if (isDestroyed) return false var params = { windowScrollTop: getScrollTop(), elementOffsetTop: _this.el.offsetTop, inViewHeight: window.innerHeight, elementOffsetTopInViewHeight: window.innerHeight - (getScrollTop() - (_this.el.offsetTop - window.innerHeight)) } if (isInView(_this.el)) { addClass(_this.el, 'inview') _this.callback.call(_this, true, params) } else { removeClass(_this.el, 'inview') _this.callback.call(_this, false, params) } } var throttledCheck = throttle(check, 100) addEvent(window, 'scroll', throttledCheck) _this.destroy = function () { isDestroyed = true removeEvent(window, 'scroll', throttledCheck) } throttledCheck() return _this } /** * @desc InView callback * * @callback scrollCallback * @param {boolean} isInView - is in view * @param {object} data - scroll data * @param {number} data.windowScrollTop - scrolled amount * @param {number} data.elementOffsetTop - element top offset * @param {number} data.inViewHeight - height of visible area * @param {number} data.elementOffsetTopInViewHeight - element top offset relative to height of visible area */ if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && module.exports) { exports = module.exports = InView } exports.InView = InView } else { root.InView = InView } })(window);