235 lines
7.4 KiB
JavaScript
235 lines
7.4 KiB
JavaScript
(function (global, factory) {
|
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
typeof define === 'function' && define.amd ? define(factory) :
|
|
(global.infiniteScroll = factory());
|
|
}(this, function () { 'use strict';
|
|
|
|
var ctx = '@@InfiniteScroll';
|
|
|
|
var throttle = function throttle(fn, delay) {
|
|
var now, lastExec, timer, context, args; //eslint-disable-line
|
|
|
|
var execute = function execute() {
|
|
fn.apply(context, args);
|
|
lastExec = now;
|
|
};
|
|
|
|
return function () {
|
|
context = this;
|
|
args = arguments;
|
|
|
|
now = Date.now();
|
|
|
|
if (timer) {
|
|
clearTimeout(timer);
|
|
timer = null;
|
|
}
|
|
|
|
if (lastExec) {
|
|
var diff = delay - (now - lastExec);
|
|
if (diff < 0) {
|
|
execute();
|
|
} else {
|
|
timer = setTimeout(function () {
|
|
execute();
|
|
}, diff);
|
|
}
|
|
} else {
|
|
execute();
|
|
}
|
|
};
|
|
};
|
|
|
|
var getScrollTop = function getScrollTop(element) {
|
|
if (element === window) {
|
|
return Math.max(window.pageYOffset || 0, document.documentElement.scrollTop);
|
|
}
|
|
|
|
return element.scrollTop;
|
|
};
|
|
|
|
var getComputedStyle = document.defaultView.getComputedStyle;
|
|
|
|
var getScrollEventTarget = function getScrollEventTarget(element) {
|
|
var currentNode = element;
|
|
// bugfix, see http://w3help.org/zh-cn/causes/SD9013 and http://stackoverflow.com/questions/17016740/onscroll-function-is-not-working-for-chrome
|
|
while (currentNode && currentNode.tagName !== 'HTML' && currentNode.tagName !== 'BODY' && currentNode.nodeType === 1) {
|
|
var overflowY = getComputedStyle(currentNode).overflowY;
|
|
if (overflowY === 'scroll' || overflowY === 'auto') {
|
|
return currentNode;
|
|
}
|
|
currentNode = currentNode.parentNode;
|
|
}
|
|
return window;
|
|
};
|
|
|
|
var getVisibleHeight = function getVisibleHeight(element) {
|
|
if (element === window) {
|
|
return document.documentElement.clientHeight;
|
|
}
|
|
|
|
return element.clientHeight;
|
|
};
|
|
|
|
var getElementTop = function getElementTop(element) {
|
|
if (element === window) {
|
|
return getScrollTop(window);
|
|
}
|
|
return element.getBoundingClientRect().top + getScrollTop(window);
|
|
};
|
|
|
|
var isAttached = function isAttached(element) {
|
|
var currentNode = element.parentNode;
|
|
while (currentNode) {
|
|
if (currentNode.tagName === 'HTML') {
|
|
return true;
|
|
}
|
|
if (currentNode.nodeType === 11) {
|
|
return false;
|
|
}
|
|
currentNode = currentNode.parentNode;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
var doBind = function doBind() {
|
|
if (this.binded) return; // eslint-disable-line
|
|
this.binded = true;
|
|
|
|
var directive = this;
|
|
var element = directive.el;
|
|
|
|
var throttleDelayExpr = element.getAttribute('infinite-scroll-throttle-delay');
|
|
var throttleDelay = 200;
|
|
if (throttleDelayExpr) {
|
|
throttleDelay = Number(directive.vm[throttleDelayExpr] || throttleDelayExpr);
|
|
if (isNaN(throttleDelay) || throttleDelay < 0) {
|
|
throttleDelay = 200;
|
|
}
|
|
}
|
|
directive.throttleDelay = throttleDelay;
|
|
|
|
directive.scrollEventTarget = getScrollEventTarget(element);
|
|
directive.scrollListener = throttle(doCheck.bind(directive), directive.throttleDelay);
|
|
directive.scrollEventTarget.addEventListener('scroll', directive.scrollListener);
|
|
|
|
this.vm.$on('hook:beforeDestroy', function () {
|
|
directive.scrollEventTarget.removeEventListener('scroll', directive.scrollListener);
|
|
});
|
|
|
|
var disabledExpr = element.getAttribute('infinite-scroll-disabled');
|
|
var disabled = false;
|
|
|
|
if (disabledExpr) {
|
|
this.vm.$watch(disabledExpr, function (value) {
|
|
directive.disabled = value;
|
|
if (!value && directive.immediateCheck) {
|
|
doCheck.call(directive);
|
|
}
|
|
});
|
|
disabled = Boolean(directive.vm[disabledExpr]);
|
|
}
|
|
directive.disabled = disabled;
|
|
|
|
var distanceExpr = element.getAttribute('infinite-scroll-distance');
|
|
var distance = 0;
|
|
if (distanceExpr) {
|
|
distance = Number(directive.vm[distanceExpr] || distanceExpr);
|
|
if (isNaN(distance)) {
|
|
distance = 0;
|
|
}
|
|
}
|
|
directive.distance = distance;
|
|
|
|
var immediateCheckExpr = element.getAttribute('infinite-scroll-immediate-check');
|
|
var immediateCheck = true;
|
|
if (immediateCheckExpr) {
|
|
immediateCheck = Boolean(directive.vm[immediateCheckExpr]);
|
|
}
|
|
directive.immediateCheck = immediateCheck;
|
|
|
|
if (immediateCheck) {
|
|
doCheck.call(directive);
|
|
}
|
|
|
|
var eventName = element.getAttribute('infinite-scroll-listen-for-event');
|
|
if (eventName) {
|
|
directive.vm.$on(eventName, function () {
|
|
doCheck.call(directive);
|
|
});
|
|
}
|
|
};
|
|
|
|
var doCheck = function doCheck(force) {
|
|
var scrollEventTarget = this.scrollEventTarget;
|
|
var element = this.el;
|
|
var distance = this.distance;
|
|
|
|
if (force !== true && this.disabled) return; //eslint-disable-line
|
|
var viewportScrollTop = getScrollTop(scrollEventTarget);
|
|
var viewportBottom = viewportScrollTop + getVisibleHeight(scrollEventTarget);
|
|
|
|
var shouldTrigger = false;
|
|
|
|
if (scrollEventTarget === element) {
|
|
shouldTrigger = scrollEventTarget.scrollHeight - viewportBottom <= distance;
|
|
} else {
|
|
var elementBottom = getElementTop(element) - getElementTop(scrollEventTarget) + element.offsetHeight + viewportScrollTop;
|
|
|
|
shouldTrigger = viewportBottom + distance >= elementBottom;
|
|
}
|
|
|
|
if (shouldTrigger && this.expression) {
|
|
this.expression();
|
|
}
|
|
};
|
|
|
|
var InfiniteScroll = {
|
|
bind: function bind(el, binding, vnode) {
|
|
el[ctx] = {
|
|
el: el,
|
|
vm: vnode.context,
|
|
expression: binding.value
|
|
};
|
|
var args = arguments;
|
|
el[ctx].vm.$on('hook:mounted', function () {
|
|
el[ctx].vm.$nextTick(function () {
|
|
if (isAttached(el)) {
|
|
doBind.call(el[ctx], args);
|
|
}
|
|
|
|
el[ctx].bindTryCount = 0;
|
|
|
|
var tryBind = function tryBind() {
|
|
if (el[ctx].bindTryCount > 10) return; //eslint-disable-line
|
|
el[ctx].bindTryCount++;
|
|
if (isAttached(el)) {
|
|
doBind.call(el[ctx], args);
|
|
} else {
|
|
setTimeout(tryBind, 50);
|
|
}
|
|
};
|
|
|
|
tryBind();
|
|
});
|
|
});
|
|
},
|
|
unbind: function unbind(el) {
|
|
if (el && el[ctx] && el[ctx].scrollEventTarget) el[ctx].scrollEventTarget.removeEventListener('scroll', el[ctx].scrollListener);
|
|
}
|
|
};
|
|
|
|
var install = function install(Vue) {
|
|
Vue.directive('InfiniteScroll', InfiniteScroll);
|
|
};
|
|
|
|
if (window.Vue) {
|
|
window.infiniteScroll = InfiniteScroll;
|
|
Vue.use(install); // eslint-disable-line
|
|
}
|
|
|
|
InfiniteScroll.install = install;
|
|
|
|
return InfiniteScroll;
|
|
|
|
})); |