用 javascript 实现一个通用分页构造器 “createPaginator“ ,内部自动维护 page 参数
·
实现 createPaginator 通用分页构造器
/**
* 通用分页构造器
* @param {Object} options 配置项
* @param {Number} Object.init - 初始页码(默认为:1)
* @param {Function} Object.before - 前置函数
* @param {Function} Object.after - 后置函数
* @param {Function} Object.handler - 操作函数
* @describe more:加载更多,page:当前页码,next:告知是否还有下一页方法, end:加载结束
*/
function createPaginator({ init, before, after, handler }) {
const _init = init ? init : 1;
let page = _init;
let _end = false;
let _more = false;
const next = (status) => {
console.log('是否还有下一页', status);
_end = !status;
if (after) after({ page, more: _more, end: _end });
};
return (more = false) => {
_more = more;
if (more) page++;
else page = _init;
if (more && _end) return;
if (before) before({ more, page });
if (handler) handler({ more, page, next });
};
}
使用 createPaginator 处理分页加载逻辑
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.content {
height: 500px;
width: 300px;
overflow-y: auto;
}
.loading {
display: none;
}
</style>
</head>
<body>
<div>
<button onclick="loadData()">重置数据</button>
</div>
<hr />
<div class="content">
<ul class="content-info">
<li>scrollingElement:在 window 滚动时使用 document.scrollingElement 确保兼容性。</li>
<li>子元素获取:对于非 window 元素,假设其第一个子元素为滚动内容主体(符合注释要求)。</li>
<li>防抖机制:通过 setTimeout + clearTimeout 实现 100ms 防抖,避免频繁触发。</li>
<li>事件捕获/冒泡:调用 event.stopPropagation() 是安全的,但注意不要影响其他逻辑。</li>
<li>返回 destroy 函数:方便后续解绑事件,防止内存泄漏。</li>
<li>scrollingElement:在 window 滚动时使用 document.scrollingElement 确保兼容性。</li>
<li>子元素获取:对于非 window 元素,假设其第一个子元素为滚动内容主体(符合注释要求)。</li>
<li>防抖机制:通过 setTimeout + clearTimeout 实现 100ms 防抖,避免频繁触发。</li>
<li>事件捕获/冒泡:调用 event.stopPropagation() 是安全的,但注意不要影响其他逻辑。</li>
<li>返回 destroy 函数:方便后续解绑事件,防止内存泄漏。</li>
</ul>
</div>
<hr />
<div class="loading"></div>
<script>
const content = document.getElementsByClassName('content')[0];
const html = document.getElementsByClassName('content-info')[0].outerHTML;
const loading = document.getElementsByClassName('loading')[0];
// 构造一个数据加载方法
const loadData = createPageHandler({
before({ more, page }) {
// 显示加载状态
loading.style.display = 'block';
loading.innerHTML = 'loading...';
},
after({ more, page, end }) {
if (end) {
// 没有更多了
loading.innerHTML = 'No More';
} else {
// 隐藏加载状态
loading.style.display = 'none';
}
},
handler({ more, page, next }) {
console.log('params', { more, page, next });
// 请求数据
getData({ name: '123456', page }).then((res) => {
if (more) {
// 追加内容
content.insertAdjacentHTML('beforeend', res);
} else {
// 替换原有内容
content.innerHTML = res;
// 回到顶部
content.scrollTo({ top: 0 });
}
// 是否还有下一页
next(res.length > 0);
});
},
});
// 滑到底部加载数据
scrollToBottom({
selector: document.getElementsByClassName('content')[0],
callback: () => loadData(true),
});
// 请求数据(模拟)
function getData(params) {
return new Promise((resolve, reject) => {
console.log('请求数据', params);
setTimeout(() => {
// 第3页前返回数据
if (params.page < 3) resolve(html);
// 第3页后没有数据了
else resolve('');
}, 2000);
});
}
/**
* 通用分页构造器
* @param {Object} options 配置项
* @param {Number} Object.init - 初始页码(默认为:1)
* @param {Function} Object.before - 前置函数
* @param {Function} Object.after - 后置函数
* @param {Function} Object.handler - 操作函数
* @describe more:加载更多,page:当前页码,next:告知是否还有下一页方法, end:加载结束
*/
function createPaginator({ init, before, after, handler }) {
const _init = init ? init : 1;
let page = _init;
let _end = false;
let _more = false;
const next = (status) => {
console.log('是否还有下一页', status);
_end = !status;
if (after) after({ page, more: _more, end: _end });
};
return (more = false) => {
_more = more;
if (more) page++;
else page = _init;
if (more && _end) return;
if (before) before({ more, page });
if (handler) handler({ more, page, next });
};
}
/**
* 页面触底执行函数(滑动元素内只能包含一个子元素)
* @param {Object} options 配置项
* @param {Element|Window} options.selector - 滑动容器元素,默认为 window
* @param {Function} options.callback - 触底时执行的回调函数
* @param {number} options.offset - 触底偏移量,用于提前触发,默认为 0
*/
function scrollToBottom({ selector = window, callback = () => {}, offset = 0 }) {
let timer = null;
let scrollTopOld = 0;
const scrollPage = (event) => {
event.stopPropagation();
// 判断是 window 还是普通元素
const isWindow = selector === window;
const scrollElement = isWindow ? document.scrollingElement || document.documentElement : selector;
// 当前滚动位置
const scrollTop = scrollElement.scrollTop;
// 视口高度(容器高度)
const viewportHeight = isWindow ? window.innerHeight : selector.clientHeight;
// 获取唯一子元素(滚动内容)
const $el = isWindow ? document.body : selector.children.length > 0 ? selector.children[0] : selector;
// 元素总高度
const elHeight = $el.scrollHeight || $el.offsetHeight;
// 判断是否接近底部
if (scrollTop + viewportHeight + offset >= elHeight) {
// 向下滚动且之前有滚动
if (scrollTopOld !== 0 && scrollTopOld < scrollTop) {
clearTimeout(timer);
timer = setTimeout(() => {
callback.call(selector); // 保持 this 指向 selector
}, 100);
}
}
scrollTopOld = scrollTop;
};
// 添加滚动事件监听
selector.addEventListener('scroll', scrollPage);
// 返回销毁函数(可选,便于解绑)
return function destroy() {
selector.removeEventListener('scroll', scrollPage);
};
}
</script>
</body>
</html>
这是一个简单实用的分页构造器,内部自动维护 page 参数
更多推荐
所有评论(0)