Skip to content

Instantly share code, notes, and snippets.

@gregoryagu
Last active July 31, 2018 02:24
Show Gist options
  • Save gregoryagu/4f01141637aa808ca063da6ae33c6989 to your computer and use it in GitHub Desktop.
Save gregoryagu/4f01141637aa808ca063da6ae33c6989 to your computer and use it in GitHub Desktop.
/**
* [jQuery-appear]{@link https://github.com/emn178/jquery-appear}
*
* @version 0.2.6
* @author Yi-Cyuan Chen [[email protected]]
* @copyright Yi-Cyuan Chen 2014-2016
* @license MIT
*/
(function ($, window, document) {
var KEY = 'jquery-appear';
var APPEAR_EVENT = 'appear';
var APPEARING_EVENT = 'appearing';
var DISAPPEAR_EVENT = 'disappear';
var EVENTS = [APPEAR_EVENT, APPEARING_EVENT, DISAPPEAR_EVENT];
var SELECTOR = ':' + KEY;
var SCROLLER_KEY = KEY + '-scroller';
var DISPLAY_KEY = KEY + '-display';
var WATCH_KEY = KEY + '-watch';
var WATCH_SELECTOR = ':' + WATCH_KEY;
var MUTATION = window.MutationObserver !== undefined;
var animationend = 'animationend webkitAnimationEnd oAnimationEnd';
var transitionend = 'transitionend webkitTransitionEnd oTransitionEnd';
var screenHeight, screenWidth, init = false, observations = $(), watchObservations = $();
console.log('jquery.appear.js loaded');
$.expr[':'][KEY] = function (element) {
return $(element).data(KEY) !== undefined;
};
$.expr[':'][WATCH_KEY] = function (element) {
return $(element).data(WATCH_KEY) !== undefined;
};
function throttle(func) {
var delay = 10;
var lastTime = 0;
var timer;
return function () {
var self = this, args = arguments;
var exec = function () {
lastTime = new Date();
func.apply(self, args);
};
if (timer) {
clearTimeout(timer);
timer = null;
}
var diff = new Date() - lastTime;
if (diff > delay) {
exec();
} else {
timer = setTimeout(exec, delay - diff);
}
};
}
function test() {
var element = $(this);
var v = element.is(':visible') && visible(this);
if (v) {
element.trigger(APPEARING_EVENT);
if (v != element.data(KEY)) {
element.trigger(APPEAR_EVENT);
}
} else if (v != element.data(KEY)) {
element.trigger(DISAPPEAR_EVENT);
}
element.data(KEY, v);
}
function visible(element) {
var rect = element.getBoundingClientRect();
return (rect.top >= 0 && rect.top <= screenHeight || rect.bottom >= 0 && rect.bottom <= screenHeight) &&
(rect.left >= 0 && rect.left <= screenWidth || rect.right >= 0 && rect.right <= screenWidth);
}
function resize() {
screenHeight = window.innerHeight || document.documentElement.clientHeight;
screenWidth = window.innerWidth || document.documentElement.clientWidth;
detect();
}
var detect = throttle(function () {
observations = observations.filter(SELECTOR);
observations.each(test);
});
function elementDetect() {
$(this).find(SELECTOR).each(test);
}
function watch() {
var element = $(this);
if (!(watchScroller(element) | watchDisplay(element))) {
return;
}
if (element.data(WATCH_KEY)) {
return;
}
element.data(WATCH_KEY, 1);
watchObservations = watchObservations.add(element);
}
function unwatch() {
var element = $(this);
if (!element.data(WATCH_KEY)) {
return;
}
if (element.find(SELECTOR).length === 0) {
element.removeData(SCROLLER_KEY).removeData(DISPLAY_KEY).removeData(WATCH_KEY);
element.unbind('scroll', elementDetect)._unbindShow(elementDetect);
}
}
function watchScroller(element) {
if (element.data(SCROLLER_KEY)) {
return false;
}
var overflow = element.css('overflow');
if (overflow != 'scroll' && overflow != 'auto') {
return false;
}
element.data(SCROLLER_KEY, 1);
element.bind('scroll', elementDetect);
return true;
}
function watchDisplay(element) {
if (MUTATION || element.data(DISPLAY_KEY)) {
return;
}
var display = element.css('display');
if (display != 'none') {
return;
}
element.data(DISPLAY_KEY, 1);
element._bindShow(elementDetect);
return true;
}
function bind(handleObj) {
var element = $(this);
if (element.is(SELECTOR)) {
return;
}
if (!init) {
init = true;
resize();
$(document).ready(function () {
$(window).on('resize', resize).on('scroll', detect);
$(document.body).on(animationend + ' ' + transitionend, detect);
});
if (MUTATION) {
var observer = new MutationObserver(detect);
observer.observe(document, {
attributes: true,
childList: true,
characterData: true,
subtree: true
});
}
}
element.data(KEY, false);
element.parents().each(watch);
// wait for handler ready
setTimeout(function () {
test.call(element[0]);
}, 1);
observations = observations.add(this);
}
function unbind(handleObj) {
var element = $(this);
setTimeout(function () {
var events = $._data(element[0], 'events') || {};
var result = false;
for (var i = 0; i < EVENTS.length; ++i) {
if (events[EVENTS[i]]) {
result = true;
break;
}
}
if (result) {
element.removeData(KEY);
watchObservations = watchObservations.filter(WATCH_SELECTOR);
watchObservations.each(unwatch);
}
}, 1);
}
function refresh(selector) {
var elements = selector === undefined ? observations : $(selector);
elements.each(function () {
var element = $(this);
if (!element.is(SELECTOR)) {
return;
}
element.parents().each(watch);
});
}
function createEvents() {
for (var i = 0; i < EVENTS.length; ++i) {
$.event.special[EVENTS[i]] = {
add: bind,
remove: unbind
};
}
}
function setEventPrefix(prefix) {
for (var i = 0; i < EVENTS.length; ++i) {
delete $.event.special[EVENTS[i]];
}
APPEAR_EVENT = prefix + 'appear';
APPEARING_EVENT = prefix + 'appearing';
DISAPPEAR_EVENT = prefix + 'disappear';
EVENTS = [APPEAR_EVENT, APPEARING_EVENT, DISAPPEAR_EVENT];
createEvents();
}
$.appear = {
check: detect,
refresh: refresh,
setEventPrefix: setEventPrefix
};
createEvents();
// SHOW EVENT
(function () {
var EVENT = 'jquery-appear-show';
var SELECTOR_KEY = KEY + '-' + EVENT;
var SELECTOR = ':' + SELECTOR_KEY;
var interval = 50, timer, observations = $();
$.expr[':'][SELECTOR_KEY] = function (element) {
return $(element).data(SELECTOR_KEY) !== undefined;
};
function test() {
var element = $(this);
var status = element.css('display') != 'none';
if (element.data(SELECTOR_KEY) != status) {
element.data(SELECTOR_KEY, status);
if (status) {
element.trigger(EVENT);
}
}
}
function detect() {
observations = observations.filter(SELECTOR);
observations.each(test);
if (observations.length === 0) {
timer = clearInterval(timer);
}
}
$.fn._bindShow = function (handler) {
this.bind(EVENT, handler);
this.data(SELECTOR_KEY, this.css('display') != 'none');
observations = observations.add(this);
if (interval && !timer) {
timer = setInterval(detect, interval);
}
};
$.fn._unbindShow = function (handler) {
this.unbind(EVENT, handler);
this.removeData(SELECTOR_KEY);
};
$.appear.setInterval = function (v) {
if (v == interval || !$.isNumeric(v) || v < 0) {
return;
}
interval = v;
timer = clearInterval(timer);
if (interval > 0) {
timer = setInterval(detect, interval);
}
};
})();
})(jQuery, window, document);
<!doctype html>
<html lang="en">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="appear.js"></script>
<meta charset="utf-8">
<title>GistRun</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Hello world!</h1>
<h1>Hello world!</h1>
<h1>Hello world!</h1>
<h1>Hello world!</h1>
<h1>Hello world!</h1>
<h1>Hello world!</h1>
<h1>Hello world!</h1>
<h1>Hello world!</h1><h1>Hello world!</h1>
<h1>Hello world!</h1>
<h1>Hello world!</h1>
<h1>Hello world!</h1>
<h1>Hello world!</h1>
<h1>Hello world!</h1><h1>Hello world!</h1>
<h1>Hello world!</h1><h1>Hello world!</h1>
<button id="appearTest">Test</button>
<script>$('#appearTest').bind('appear', function () { console.log('appear event') }); </script>
</body>
</html>
/* todo: add styles */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment