123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- import throttle from 'throttle-debounce/debounce';
- import {
- isHtmlElement,
- isFunction,
- isUndefined,
- isDefined
- } from 'element-ui/src/utils/types';
- import {
- getScrollContainer
- } from 'element-ui/src/utils/dom';
- const getStyleComputedProperty = (element, property) => {
- if (element === window) {
- element = document.documentElement;
- }
- if (element.nodeType !== 1) {
- return [];
- }
- // NOTE: 1 DOM access here
- const css = window.getComputedStyle(element, null);
- return property ? css[property] : css;
- };
- const entries = (obj) => {
- return Object.keys(obj || {})
- .map(key => ([key, obj[key]]));
- };
- const getPositionSize = (el, prop) => {
- return el === window || el === document
- ? document.documentElement[prop]
- : el[prop];
- };
- const getOffsetHeight = el => {
- return getPositionSize(el, 'offsetHeight');
- };
- const getClientHeight = el => {
- return getPositionSize(el, 'clientHeight');
- };
- const scope = 'ElInfiniteScroll';
- const attributes = {
- delay: {
- type: Number,
- default: 200
- },
- distance: {
- type: Number,
- default: 0
- },
- disabled: {
- type: Boolean,
- default: false
- },
- immediate: {
- type: Boolean,
- default: true
- }
- };
- const getScrollOptions = (el, vm) => {
- if (!isHtmlElement(el)) return {};
- return entries(attributes).reduce((map, [key, option]) => {
- const { type, default: defaultValue } = option;
- let value = el.getAttribute(`infinite-scroll-${key}`);
- value = isUndefined(vm[value]) ? value : vm[value];
- switch (type) {
- case Number:
- value = Number(value);
- value = Number.isNaN(value) ? defaultValue : value;
- break;
- case Boolean:
- value = isDefined(value) ? value === 'false' ? false : Boolean(value) : defaultValue;
- break;
- default:
- value = type(value);
- }
- map[key] = value;
- return map;
- }, {});
- };
- const getElementTop = el => el.getBoundingClientRect().top;
- const handleScroll = function(cb) {
- const { el, vm, container, observer } = this[scope];
- const { distance, disabled } = getScrollOptions(el, vm);
- if (disabled) return;
- const containerInfo = container.getBoundingClientRect();
- if (!containerInfo.width && !containerInfo.height) return;
- let shouldTrigger = false;
- if (container === el) {
- // be aware of difference between clientHeight & offsetHeight & window.getComputedStyle().height
- const scrollBottom = container.scrollTop + getClientHeight(container);
- shouldTrigger = container.scrollHeight - scrollBottom <= distance;
- } else {
- const heightBelowTop = getOffsetHeight(el) + getElementTop(el) - getElementTop(container);
- const offsetHeight = getOffsetHeight(container);
- const borderBottom = Number.parseFloat(getStyleComputedProperty(container, 'borderBottomWidth'));
- shouldTrigger = heightBelowTop - offsetHeight + borderBottom <= distance;
- }
- if (shouldTrigger && isFunction(cb)) {
- cb.call(vm);
- } else if (observer) {
- observer.disconnect();
- this[scope].observer = null;
- }
- };
- export default {
- name: 'InfiniteScroll',
- inserted(el, binding, vnode) {
- const cb = binding.value;
- const vm = vnode.context;
- // only include vertical scroll
- const container = getScrollContainer(el, true);
- const { delay, immediate } = getScrollOptions(el, vm);
- const onScroll = throttle(delay, handleScroll.bind(el, cb));
- el[scope] = { el, vm, container, onScroll };
- if (container) {
- container.addEventListener('scroll', onScroll);
- if (immediate) {
- const observer = el[scope].observer = new MutationObserver(onScroll);
- observer.observe(container, { childList: true, subtree: true });
- onScroll();
- }
- }
- },
- unbind(el) {
- const { container, onScroll } = el[scope];
- if (container) {
- container.removeEventListener('scroll', onScroll);
- }
- }
- };
|