143 lines
5.5 KiB
JavaScript
143 lines
5.5 KiB
JavaScript
|
/**
|
||
|
* author Christopher Blum
|
||
|
* - based on the idea of Remy Sharp, http://remysharp.com/2009/01/26/element-in-view-event-plugin/
|
||
|
* - forked from http://github.com/zuk/jquery.inview/
|
||
|
*/
|
||
|
(function ($) {
|
||
|
var inviewObjects = {}, viewportSize, viewportOffset,
|
||
|
d = document, w = window, documentElement = d.documentElement, expando = $.expando;
|
||
|
|
||
|
$.event.special.inview = {
|
||
|
add: function(data) {
|
||
|
inviewObjects[data.guid + "-" + this[expando]] = { data: data, $element: $(this) };
|
||
|
},
|
||
|
|
||
|
remove: function(data) {
|
||
|
try { delete inviewObjects[data.guid + "-" + this[expando]]; } catch(e) {}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
function getViewportSize() {
|
||
|
var mode, domObject, size = { height: w.innerHeight, width: w.innerWidth };
|
||
|
|
||
|
// if this is correct then return it. iPad has compat Mode, so will
|
||
|
// go into check clientHeight/clientWidth (which has the wrong value).
|
||
|
if (!size.height) {
|
||
|
mode = d.compatMode;
|
||
|
if (mode || !$.support.boxModel) { // IE, Gecko
|
||
|
domObject = mode === 'CSS1Compat' ?
|
||
|
documentElement : // Standards
|
||
|
d.body; // Quirks
|
||
|
size = {
|
||
|
height: domObject.clientHeight,
|
||
|
width: domObject.clientWidth
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
function getViewportOffset() {
|
||
|
return {
|
||
|
top: w.pageYOffset || documentElement.scrollTop || d.body.scrollTop,
|
||
|
left: w.pageXOffset || documentElement.scrollLeft || d.body.scrollLeft
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function checkInView() {
|
||
|
var $elements = $(), elementsLength, i = 0;
|
||
|
|
||
|
$.each(inviewObjects, function(i, inviewObject) {
|
||
|
var selector = inviewObject.data.selector,
|
||
|
$element = inviewObject.$element;
|
||
|
$elements = $elements.add(selector ? $element.find(selector) : $element);
|
||
|
});
|
||
|
|
||
|
elementsLength = $elements.length;
|
||
|
if (elementsLength) {
|
||
|
viewportSize = viewportSize || getViewportSize();
|
||
|
viewportOffset = viewportOffset || getViewportOffset();
|
||
|
|
||
|
for (; i<elementsLength; i++) {
|
||
|
// Ignore elements that are not in the DOM tree
|
||
|
if (!$.contains(documentElement, $elements[i])) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
var element = $elements[i],
|
||
|
$element = $(element),
|
||
|
elementSize = {},
|
||
|
elementOffset = {},
|
||
|
inView = $element.data('inview'),
|
||
|
visiblePartX,
|
||
|
visiblePartY,
|
||
|
visiblePartsMerged;
|
||
|
|
||
|
// for the case where 'display:none' is used in place of 'visibility:hidden'
|
||
|
// count and sum the above items to get and move closer to the correct values
|
||
|
// IMPORTANT :: insert element into container empty
|
||
|
if($element.css('display') == 'none')
|
||
|
{
|
||
|
var parentElement = $element.parent();
|
||
|
|
||
|
elementOffset.top = parentElement.offset().top;
|
||
|
elementOffset.left = parentElement.offset().left;
|
||
|
elementSize.height = parentElement.height();
|
||
|
elementSize.width = parentElement.width();
|
||
|
} else {
|
||
|
elementSize = { height: $element.height(), width: $element.width() }
|
||
|
elementOffset = $element.offset();
|
||
|
}
|
||
|
|
||
|
// Don't ask me why because I haven't figured out yet:
|
||
|
// viewportOffset and viewportSize are sometimes suddenly null in Firefox 5.
|
||
|
// Even though it sounds weird:
|
||
|
// It seems that the execution of this function is interferred by the onresize/onscroll event
|
||
|
// where viewportOffset and viewportSize are unset
|
||
|
if (!viewportOffset || !viewportSize) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (element.offsetWidth >= 0 && element.offsetHeight >= 0 && element.style.display != "none" &&
|
||
|
elementOffset.top + elementSize.height > viewportOffset.top &&
|
||
|
elementOffset.top < viewportOffset.top + viewportSize.height &&
|
||
|
elementOffset.left + elementSize.width > viewportOffset.left &&
|
||
|
elementOffset.left < viewportOffset.left + viewportSize.width) {
|
||
|
visiblePartX = (viewportOffset.left > elementOffset.left ?
|
||
|
'right' : (viewportOffset.left + viewportSize.width) < (elementOffset.left + elementSize.width) ?
|
||
|
'left' : 'both');
|
||
|
visiblePartY = (viewportOffset.top > elementOffset.top ?
|
||
|
'bottom' : (viewportOffset.top + viewportSize.height) < (elementOffset.top + elementSize.height) ?
|
||
|
'top' : 'both');
|
||
|
visiblePartsMerged = visiblePartX + "-" + visiblePartY;
|
||
|
if (!inView || inView !== visiblePartsMerged) {
|
||
|
$element.data('inview', visiblePartsMerged).trigger('inview', [true, visiblePartX, visiblePartY]);
|
||
|
}
|
||
|
} else if (inView) {
|
||
|
$element.data('inview', false).trigger('inview', [false]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$(w).bind("scroll resize", function() {
|
||
|
viewportSize = viewportOffset = null;
|
||
|
});
|
||
|
|
||
|
// IE < 9 scrolls to focused elements without firing the "scroll" event
|
||
|
if (!documentElement.addEventListener && documentElement.attachEvent) {
|
||
|
documentElement.attachEvent("onfocusin", function() {
|
||
|
viewportOffset = null;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// Use setInterval in order to also make sure this captures elements within
|
||
|
// "overflow:scroll" elements or elements that appeared in the dom tree due to
|
||
|
// dom manipulation and reflow
|
||
|
// old: $(window).scroll(checkInView);
|
||
|
//
|
||
|
// By the way, iOS (iPad, iPhone, ...) seems to not execute, or at least delays
|
||
|
// intervals while the user scrolls. Therefore the inview event might fire a bit late there
|
||
|
setInterval(checkInView, 250);
|
||
|
})(jQuery);
|