lu2
Version:
Simple and flexible UI component library based on native HTML and JavaScript
444 lines (385 loc) • 16 kB
JavaScript
/**
* @Pagination.js
* @author zhangxinxu
* @version
* @Created: 15-06-26
* @edit: 19-09-10 native js rewrite
*/
/* global module */
(function (global, factory) {
if (typeof exports === 'object' && typeof module !== 'undefined') {
module.exports = factory();
} else if (typeof define === 'function' && (define.amd || define.cmd)) {
define(factory);
} else {
global.Pagination = factory();
}
// eslint-disable-next-line
}((typeof global !== 'undefined') ? global
// eslint-disable-next-line
: ((typeof window !== 'undefined') ? window
: ((typeof self !== 'undefined') ? self : this)), function () {
/**
* 分页组件
* 支持new实例构造
* eg new Pagination(element, options);
**/
// ui类名
// 类名变量
var CL = {
add: function () {
return ['ui-page'].concat([].slice.call(arguments)).join('-');
},
join: function () {
return CL + ' ' + this.add.apply(this, arguments);
},
toString: function () {
return 'ui-page';
}
};
/**
* Ajax分页组件
* @param {[type]} element [description]
* @param {[type]} options [description]
*/
var Pagination = function (element, options) {
// 默认参数
var defaults = {
// 总的数据量
total: 0,
// 当前的页码
current: 1,
// 每页显示的数目
per: 15,
// short 长分页还是短分页
mode: 'long',
// href也可以是动态Function
href: null,
onClick: function () {}
};
// 元素的获取与判断
var elePagination = element;
if (typeof element == 'string') {
elePagination = document.querySelector(element);
}
// 如果分页容器元素不存在,隐藏
if (elePagination) {
// 参数还可以取自element元素
var objParamsFromElement = {};
for (var keyParam in defaults) {
var strAttr = element.getAttribute(keyParam);
if (typeof strAttr != 'string') {
strAttr = elePagination.dataset[keyParam];
}
if (typeof strAttr == 'string') {
objParamsFromElement[keyParam] = strAttr;
}
}
// 避如果已经初始化
if (elePagination.data && elePagination.data.pagination) {
return elePagination.data.pagination;
}
}
// 可选参数整合
var objParams = Object.assign({}, defaults, objParamsFromElement, options || {});
// 数据的暴露
this.params = Object.assign({}, objParams);
// onClick作为callback暴露
delete this.params.onClick;
// 回调暴露
this.callback = {
click: objParams.onClick
};
// 元素暴露
this.element = {
container: elePagination,
pagination: elePagination
};
// 前后分页使用的内联SVG代码
this.svg = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200"><path d="M85.876,100.5l49.537-50.526c4.089-4.215,4.089-11.049,0-15.262 c-4.089-4.218-10.719-4.218-14.808,0L63.586,92.868c-4.089,4.215-4.089,11.049,0,15.264l57.018,58.156 c4.089,4.215,10.719,4.215,14.808,0c4.089-4.215,4.089-11.049,0-15.262L85.876,100.5z"/></svg>';
if (!elePagination) {
return this;
}
// 事件
this.events();
// 呈现
this.show();
// 存储
if (!elePagination.data) {
elePagination.data = {};
}
elePagination.data.pagination = this;
// 自定义元素的属性
Object.keys(this.params).forEach(function (keyParam) {
Object.defineProperty(elePagination, keyParam, {
writeable: true,
enumerable: true,
get: function () {
return this.getAttribute(keyParam) || (function () {
if (this.data && this.data.pagination) {
return this.data.pagination.params[keyParam] || null;
}
return null;
})();
},
set: function (value) {
this.setAttribute(keyParam, value);
// 模板属性变化了,重渲染
if (this.data && this.data.pagination) {
this.data.pagination.params[keyParam] = value;
this.data.pagination.show();
}
}
});
});
};
/**
* 分页相关的事件处理
* @return {[type]} [description]
*/
Pagination.prototype.events = function () {
var elePagination = this.element.pagination;
// 委托点击事件
elePagination.addEventListener('click', function (event) {
// 一些元素
var eleTarget = event.target;
// 目标点击元素
var eleClicked = eleTarget.closest('a');
// 未获取到元素返回
if (!eleClicked) {
return;
}
var numCurrent = eleClicked.getAttribute('data-page');
// 改变全局暴露的current值
this.params.current = numCurrent * 1;
// 外部的 HTML 属性也同步
if (elePagination.hasAttribute('current')) {
elePagination.setAttribute('current', numCurrent);
} else if (elePagination.hasAttribute('data-current')) {
elePagination.setAttribute('data-current', numCurrent);
}
// 当前的类名,onClick回调可以准确识别点击DOM元素
var strClassName = eleClicked.className;
// 分页刷新
this.show();
// 此时由于HTML刷新,原来的eleClicked元素已经不复存在
// 因此需要重新获取一遍
if (/prev/.test(strClassName)) {
eleClicked = elePagination.querySelector('.' + CL.add('prev'));
} else if (/next/.test(strClassName)) {
eleClicked = elePagination.querySelector('.' + CL.add('next'));
} else {
eleClicked = elePagination.querySelector('.' + CL.add('current'));
}
// aria以及键盘访问
if (eleClicked) {
eleClicked.focus();
if (window.isKeyEvent == false) {
eleClicked.blur();
}
if (/^javascript/.test(eleClicked.href)) {
event.preventDefault();
}
}
this.callback.click.call(this, eleClicked, this.params.current);
// 触发自定义事件
elePagination.dispatchEvent(new CustomEvent('change'));
}.bind(this));
};
/**
* 创建分页字符串的方法
* @param {Object} params 分页相关的数据对象
* @param {String} mode 分页的类型
* @return {String} 返回拼接好的HTML字符串
*/
Pagination.prototype.create = function () {
var objParams = this.params;
// 分页一些数据
var numTotal = objParams.total * 1 || 0;
var numCurrent = objParams.current * 1 || 1;
var numPer = objParams.per * 1 || 1;
// href的支持与处理
var strOrFunHref = objParams.href || 'javascript:';
var funHref = function (index) {
if (typeof strOrFunHref == 'function') {
return strOrFunHref(index);
}
return strOrFunHref.toString().replace('${current}', index);
};
// 计算总页数
var numPageSize = Math.ceil(numTotal / numPer) || 1;
// 分页HTML内容
var strHtmlPage = '';
// 1. 首先是通用的前后翻页
if (numCurrent > 1) {
strHtmlPage = strHtmlPage + '<a href="' + funHref(numCurrent - 1) + '" class="' + CL.join('prev') + '" data-page="' + (numCurrent - 1) + '" aria-label="上一页,当前第' + numCurrent + '页">' + this.svg + '</a>';
} else {
// 当前是第一页,自然不能往前翻页
strHtmlPage = strHtmlPage + '<span class="' + CL.join('prev') + '">' + this.svg + '</span>';
}
// 2. 中间部分
// 短分页和长分页
var numVisible = 6;
if (objParams.mode == 'long') {
var funLong = function (numStart) {
if (numStart == numCurrent) {
strHtmlPage = strHtmlPage + '<span class="' + CL.join('current') + '" aria-label="第' + numStart + '页,共' + numPageSize + '页" aria-selected="true" role="option">' + numStart + '</span>';
} else {
strHtmlPage = strHtmlPage + '<a href="' + funHref(numStart) + '" class="' + CL + '" data-page="' + numStart + '" aria-label="第' + numStart + '页,共' + numPageSize + '页">' + numStart + '</a>';
}
};
/**
* 为了保证体验,在分页数量大的时候,要保证总条目数为7个
假设页码总数20,规则如下
当前是1: 则 1-5,... 20
2: 则 1-5,...,20
3: 则 1-5,...,20
4: 则 1-5,...,20
5: 则 1...456,...,20
...
参见下面的else if的处理
*/
if (numPageSize <= numVisible) {
// 如果页面不大于6个,全部显示
for (var start = 1; start <= numPageSize; start++) {
funLong(start);
}
} else if (numCurrent < numPageSize * 0.5 && numCurrent < numVisible - 1) {
// 至少保证current的前后有数据
if (numCurrent < numVisible - 1) {
for (start = 1; start < numVisible; start++) {
funLong(start);
}
}
strHtmlPage = strHtmlPage + '<span class="' + CL.join('ellipsis') + '">...</span>';
funLong(numPageSize);
} else if (numCurrent > numPageSize * 0.5 && numCurrent > numPageSize - numVisible + 2) {
funLong(1);
strHtmlPage = strHtmlPage + '<span class="' + CL.join('ellipsis') + '">...</span>';
for (start = numPageSize - numVisible + 2; start <= numPageSize; start++) {
funLong(start);
}
} else {
// 前后两处打点
funLong(1);
strHtmlPage = strHtmlPage + '<span class="' + CL.join('ellipsis') + '">...</span>';
funLong(numCurrent - 1);
funLong(numCurrent);
funLong(numCurrent + 1);
strHtmlPage = strHtmlPage + '<span class="' + CL.join('ellipsis') + '">...</span>';
funLong(numPageSize);
}
} else if (objParams.mode == 'short') {
strHtmlPage = strHtmlPage + '<span class="' + CL.join('text') + '" aria-label="第' + numCurrent + '页,共' + numPageSize + '页" role="option">' + [numCurrent, numPageSize].join('/') + '</span>';
}
// 3. 往后翻页
if (numCurrent < numPageSize) {
strHtmlPage = strHtmlPage + '<a href="' + funHref(numCurrent + 1) + '" class="' + CL.join('next') + '" data-page="' + (numCurrent + 1) + '" aria-label="下一页,当前第' + numCurrent + '页">' + this.svg + '</a>';
} else {
strHtmlPage = strHtmlPage + '<span class="' + CL.join('next') + '">' + this.svg + '</span>';
}
return '<div class="' + CL.add('x') + '">' + strHtmlPage + '</div>';
};
/**
* 分页显示(也是刷新方法)
* @return {Object} 返回当前实例
*/
Pagination.prototype.show = function () {
// 数据合法性处理
var objParams = this.params;
objParams.total = Math.max(objParams.total, 0);
objParams.per = Math.max(objParams.per, 0);
// current合法性处理
var numMaxCurrent = Math.ceil(objParams.total / objParams.per);
if (objParams.current > numMaxCurrent) {
objParams.current = numMaxCurrent;
} else if (objParams.current < 1) {
objParams.current = 1;
}
// 更新分页的HTML内容
this.element.pagination.innerHTML = this.create();
return this;
};
/**
* 分页数据刷新,根据元素属性变化
* @return {[type]} [description]
*/
Pagination.prototype.refresh = function () {
var objParams = this.params;
// 元素
var elePagination = this.element.pagination;
for (var keyParam in objParams) {
if (elePagination.hasAttribute(keyParam)) {
this.params[keyParam] = elePagination.getAttribute(keyParam);
}
}
this.show();
};
/**
* 支持自定义的<ui-pagination>元素
* @return {[type]} [description]
*/
var funAutoInitAndWatching = function () {
var strSelector = 'ui-pagination, [is-pagination]';
// 遍历页面上所有的<ui-pagination>元素
document.querySelectorAll(strSelector).forEach(function (elePagination) {
new Pagination(elePagination);
});
if (window.watching === false) {
return;
}
// 同步更新分页内容的方法
var funSyncRefresh = function (node) {
if (node.nodeType != 1 || node.matches(strSelector) == false) {
return;
}
// 元素
var elePagination = node;
// 实例对象
var objPagination = elePagination.data && elePagination.data.pagination;
// 有则刷新无则初始化
if (!objPagination) {
new Pagination(elePagination);
} else {
// 更新
objPagination.refresh();
}
};
// DOM Insert检测自动初始化
// IE11+使用MutationObserver
// IE9-IE10使用Mutation Events
if (window.MutationObserver) {
var observerSelect = new MutationObserver(function (mutationsList) {
mutationsList.forEach(function (mutation) {
if (mutation.type == 'childList') {
mutation.addedNodes.forEach(function (eleAdd) {
if (eleAdd.matches && eleAdd.matches(strSelector)) {
funSyncRefresh(eleAdd);
} else if (eleAdd.querySelector) {
eleAdd.querySelectorAll(strSelector).forEach(function (elePagination) {
funSyncRefresh(elePagination);
});
}
});
}
});
});
observerSelect.observe(document.body, {
childList: true,
subtree: true
});
} else {
document.body.addEventListener('DOMNodeInserted', function (event) {
// 插入节点
funSyncRefresh(event.target);
});
}
};
// 监听-免初始绑定
if (document.readyState != 'loading') {
funAutoInitAndWatching();
} else {
window.addEventListener('DOMContentLoaded', funAutoInitAndWatching);
}
return Pagination;
}));