【前端技巧】JS自定义指令 —— 无限滚动(改写el-table-infinite-scroll)
@Author:Outman
@Date:2023-03-08
JS自定义指令 —— 无限滚动(改写el-table-infinite-scroll)
自定义指令实现
// directives/index.js
/*** 此处引入element官方的infinite-scroll,'./infinite-scroll/index'此处的index并非为当前的index.js文件* 整体目录结构为:* · directives* · index.js (当前文件)* · infinite-scroll (element官方的infinite-scroll,本文文末已提供)* · index.js* · src* · main.js*/
import InfiniteScroll from "./infinite-scroll/index";
export default {// 全局挂载install(Vue) {/*** @description: 无限滚动* @param scrollClass String 需要添加无限滚动的节点class* scrollClass默认为".el-table__body-wrapper",可自行修改为所需绑定无限滚动节点的class* 例如:element-table中若要实现无限滚动,则需要绑定class为el-table__body-wrapper* 无限滚动相关配置项(elementUI相关配置):* infinite-scroll-disabled 是否禁用 boolean - false* infinite-scroll-delay 节流时延,单位为ms number - 200* infinite-scroll-distance 触发加载的距离阈值,单位为px number - 0* infinite-scroll-immediate 是否立即执行加载方法,以防初始状态下内容无法撑满容器 boolean - true*/const elScope = 'InfiniteScroll'; // scope nameVue.directive('outman-infinite-scroll', {inserted(el, binding, vnode, oldVnode){const value = binding.value || {};const { scrollClass = ".el-table__body-wrapper" } = value;// 获取 hui table 中的滚动层 const scrollElement = el.querySelector(scrollClass);if(!scrollElement)throw '找不到指定的容器!'// 设置自动滚动scrollElement.style.overflowY = 'auto';// dom 渲染后setTimeout(()=>{// 高度默认值赋值if (!el.style.height) {scrollElement.style.height = '300px';}// 获取配置参数asyncElOptions(vnode, el, scrollElement);// 绑定 infinite-scrollInfiniteScroll.inserted(scrollElement, binding, vnode);// 将子集的引用放入 el 上,用于 unbind 中销毁事件el[elScope] = scrollElement[elScope];}, 0)},componentUpdated(el, binding, vnode){const value = binding.value || {};const { scrollClass = ".h-table-body" } = value;// 更新配置参数asyncElOptions(vnode, el, el.querySelector(scrollClass));},unbind(el){InfiniteScroll.unbind(el)}})/*** 同步 el-infinite-scroll 的配置项* @param sourceVNode* @param sourceElem* @param targetElem*/function asyncElOptions(sourceVNode, sourceElem, targetElem) {let value;['disabled', 'delay', 'immediate'].forEach((name) => {name = 'infinite-scroll-' + name;value = sourceElem.getAttribute(name);if (value !== null) {targetElem.setAttribute(name, value);} else {targetElem.removeAttribute(name);}});// fix: windows/chrome 的 scrollTop + clientHeight 与 scrollHeight 不一致的 BUGconst name = 'infinite-scroll-distance';value = sourceElem.getAttribute(name);targetElem.setAttribute(name, value < 1 ? 1 : value);}}
};
自定义指令引入
// 引入app.js
import directives from '../src/directives/index';
Vue.use(directives);
自定义指令应用
<!-- 应用 -->
<template><el-table height="200" :columns="columns" :data="data" v-outman-infinite-scroll="handleInfinite" :infinite-scroll-delay="200":infinite-scroll-disabled="busy" :infinite-scroll-distance="10"></el-table>
</template>
<script>
export default {data() {return {busy: false,columns: [{title: '姓名',key: 'name'},{title: '年龄',key: 'age'},{title: '地址',key: 'address'}],data: [{name: '王小明',age: 18,address: '北京市朝阳区芍药居'},{name: '张小刚',age: 25,address: '北京市海淀区西二旗'},{name: '李小红',age: 30,address: '上海市浦东新区世纪大道'},{name: '周小伟',age: 26,address: '深圳市南山区深南大道'},{name: '王小明',age: 18,address: '北京市朝阳区芍药居'},{name: '张小刚',age: 25,address: '北京市海淀区西二旗'}]};},methods: {handleInfinite() {console.log('我在无限滚动了!');}}
};
</script>
el-infinite-scroll 源码(上述infinite-scroll目录内的文件)
// infinite/index.js
import InfiniteScroll from './src/main.js';/* istanbul ignore next */
InfiniteScroll.install = function(Vue) {Vue.directive(InfiniteScroll.name, InfiniteScroll);
};export default InfiniteScroll;// infinite/src/main.js
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 hereconst 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().heightconst 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 scrollconst 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);}}
};
【前端技巧】JS自定义指令 —— 无限滚动(改写el-table-infinite-scroll)相关推荐
- js:Vue.js自定义指令实现scroll下滑滚动翻页
Vue.js自定义指令实现scroll下滑滚动翻页 核心代码 import Vue from 'vue'// v-scroll Vue.directive('scroll', {bind(el, bi ...
- Vue.js自定义指令的用法与实例
市面上大多数关于Vue.js自定义指令的文章都在讲语法,很少讲实际的应用场景和用例,以致于即便明白了怎么写,也不知道怎么用.本文不讲语法,就讲自定义指令的用法. 自定义指令是用来操作DOM的.尽管Vu ...
- JS 自定义实现数字滚动处理
一.浏览器端js自定义实现数字滚动 使用示例: <!DOCTYPE html> <html lang="en"><head><meta c ...
- Vue踩坑之旅(四)—— 自定义指令实现滚动加载
这几天在做商城首页的商品列表,商品卡片的数量很多,如果一次性加载那么多,加载较慢,而且用户体验不好.所以使用鼠标无限滚动加载效果更好. 实现滚动加载的方式有很多,有现成的组件 InfiniteScro ...
- (13)Vue.js 自定义指令
一.Vue.js之定义指令介绍 前面我们学习了一些Vue.js给我们提供的指令,那么我们知道指令就是帮助我们去简化DOM操作的,相当于对基础 DOM操作的一种封装.那么我们仅仅使用这些Vue.js提供 ...
- angular.js自定义指令
angular.js最为强大的地方在于可以通过自定义指令来扩展html元素,这种思路与JSP的taglib类似,但在实现细节上更为自由,并且自定义指令也可以提供表单元素交互.数据绑定.事件处理功能. ...
- Vue.js 自定义指令
简介 除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令.注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件.然而,有的情况下,你仍然需要对普通 D ...
- 前端技巧-JS元编程ES6 symbol公开符号
元编程就是指以操作目标为程序本身的行为特性的编程,而在ES6中增加了类型symbol,除了自定义的符号之外,还预定义了其他的一些内置符号,可以被称为内置符号.下面就来给大家介绍一下这些内置符号. 1. ...
- 无限滚动重置服务器,简单无限滚动的实现
在使用elementUI组件库的时候,用到了无限滚动这个功能.我没有看源码,直接在网上学习了下实现的思路,然后自己手动编码以下.在此总结下. 假设页面上有一个盒子容器,容器内有一些子元素.容器的高度是 ...
最新文章
- HTTP协议解析之Cookie
- 秦州:西瓜书 + 南瓜书 吃瓜系列 10. 集成学习(下)
- 浅谈强化学习的方法及学习路线
- oracle 数组的用法,oracle存储过程中数组的使用
- 2041. 干草堆(前缀和差分)
- android mtk平台,android mtk平台默认输入法
- 去除内联元素之间的间距
- 编写可靠shell脚本的8个建议
- 计算机网络基础问题总结
- 人工智能十大算法_【读书】人工智能和大数据:新智能的诞生/人工智能科学与技术丛书...
- 2013年最新十大xp系统下载排行榜-无极系统下载站
- Linux网络编程必学的TCP/IP协议——图解分层(通俗易懂)【建议新手收藏】
- 【C语言典例】——day8:猜名次
- 《数学之美》吴军-读书笔记
- iPhoneX、iPhoneXS、iPhoneXR、iPhoneXSMax屏幕适配尺寸@media
- 怎么把荣耀8x的手机备忘录导到电脑里?
- 护卫神备份mysql_护卫神好备份系统数据库怎么备份?
- dba招生_深圳DBA项目招生简介
- android 功能模块之通讯模块四
- 中关村被骗记(维权全过程)