javascript 可拖动table列宽的library
demo
http://uso.oschina.io/resizable-colmuns-table
github地址
https://github.com/uso51984/resizable-columns-table
调用方法
createColResizable(domElemTable,options)
options = {
liveDrag: true, // 是否实时拖动
defaultMinWidth: 30, //默认没列最小宽度
headerOnly: true, // 拖动竖线是否只有thead
disabledColumns: [], //不能拖动的th
onResizing: null, // 正在拖动callback
onResized: null // 拖动结束callback
}
# 每列单独宽度需要在对应th加入 自定义属性 data-min-width="number", eg: data-min-width="300"
- domElem 传入table dom元素
- options 对象(可选)
table 结构
<table>
<thead>
<tr>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Mark</td>
</tr>
</tbody>
</table>
# 例如
<table>
<thead>
<tr>
<th>#</th>
<th>First Name</th>
<th>Last Name</th>
<th>Username</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">1</th>
<td>Mark</td>
<td>Otto</td>
<td>@mdo</td>
</tr>
<tr>
<th scope="row">2</th>
<td>Jacob</td>
<td>Thornton</td>
<td>@fat</td>
</tr>
<tr>
<th scope="row">3</th>
<td>Larry</td>
<td>the Bird</td>
<td>@twitter</td>
</tr>
</tbody>
</table>
代码
index.js
import isElement from 'lodash/isElement';
import ColResizable from './colResizable';
import '../style/index.less';
const createColResizable = (domEleTable, options) => {
if (isElement(domEleTable) && domEleTable.nodeName === 'TABLE') {
return domEleTable.__resizable ||
(domEleTable.__resizable = new ColResizable(domEleTable, options));
}
return null;
};
export default createColResizable;
index.js
import isFunction from 'lodash/isFunction';
import isArray from 'lodash/isArray';
import {
tryParseInt,
removeClass,
addClass
} from './utils';
export default class ColResizable {
static defaults = {
liveDrag: true,
defaultMinWidth: 30,
headerOnly: true,
disabledColumns: [],
onResizing: null,
onResized: null
};
constructor(domElmTable, options = {}) {
this.options = { ...ColResizable.defaults, ...options };
this.domElmTable = domElmTable;
this.onGripMouseDown = this.onGripMouseDown.bind(this);
this.onMouseMove = this.onMouseMove.bind(this);
this.onMouseUp = this.onMouseUp.bind(this);
this.init();
}
init() {
addClass(this.domElmTable, 'table-col-resizer');
this.domElmHandleList = [];
this.domElmTableTheadThList = [];
this.tableWidth = `${this.domElmTable.offsetWidth}px`;
this.cellSpacing = tryParseInt(getComputedStyle(this.domElmTable).getPropertyValue('border-spacing'));
this.borderLeftWidth = tryParseInt(getComputedStyle(this.domElmTable).getPropertyValue('border-left-width'));
this.createGrips();
}
createGrips() {
const thList = this.domElmTable.querySelectorAll('thead th');
const domElmThList = [];
this.domElmHandleContainer = this.domElmTable.previousSibling;
const hasHandleContainer = this.domElmHandleContainer && this.domElmHandleContainer.className === 'col-resize-container';
if (!hasHandleContainer) {
this.domElmTable.insertAdjacentHTML('beforebegin', '<div class="col-resize-container"/>');
this.domElmHandleContainer = this.domElmTable.previousSibling;
} else {
Array.prototype.push.apply(this.domElmHandleList, this.domElmHandleContainer.childNodes);
}
Array.prototype.push.apply(domElmThList, thList);
this.thLength = domElmThList.length;
this.lastThIndex = this.thLength - 1;
let { disabledColumns } = this.options;
if (!isArray(disabledColumns)) {
disabledColumns = [];
}
domElmThList.forEach((domElmTh, index) => {
const disabledColumn = disabledColumns.indexOf(index) !== -1;
let domElmHandle;
if (!hasHandleContainer) {
this.domElmHandleContainer.insertAdjacentHTML('beforeend',
`<div class="drag-handle">
<i class="icon icon-caret-right"></i>
<div class="col-resizer"></div>
<i class="icon icon-caret-left"></i>
</div>`
);
domElmHandle = this.domElmHandleContainer.lastChild;
} else {
domElmHandle = this.domElmHandleList[index];
}
if (index === this.lastThIndex && !hasHandleContainer) {
addClass(domElmHandle, 'last-handle');
}
if (!disabledColumn && !hasHandleContainer) {
domElmHandle.addEventListener('mousedown', this.onGripMouseDown);
} else if (disabledColumn && !hasHandleContainer) {
addClass(domElmHandle, 'disabled-drag');
}
domElmHandle.index = index;
domElmTh.w = domElmTh.offsetWidth;
domElmTh.style.width = `${domElmTh.offsetWidth}px`;
if (!hasHandleContainer) {
this.domElmHandleList.push(domElmHandle);
}
this.domElmTableTheadThList.push(domElmTh);
});
this.syncGrips();
}
syncGrips() {
const { headerOnly } = this.options;
const theadHight = this.domElmTableTheadThList[0].offsetHeight;
let height;
if (headerOnly) {
height = theadHight;
} else {
height = this.domElmTable.offsetHeight;
}
for (let i = 0; i < this.thLength; i += 1) {
const domElmTh = this.domElmTableTheadThList[i];
let left;
if (i === 0) {
left = domElmTh.offsetWidth + (this.cellSpacing / 2);
} else {
const handleColLeft = this.domElmHandleList[i - 1].style.left + (this.cellSpacing / 2);
left = tryParseInt(handleColLeft) + domElmTh.offsetWidth;
}
this.domElmHandleList[i].style.left = `${left}px`;
this.domElmHandleList[i].style.height = `${height}px`;
}
const domElmIconList = [];
const iconHeight = this.domElmHandleContainer.querySelector('.col-resize-container .icon').offsetHeight;
const domElemIcons = this.domElmHandleContainer.querySelectorAll('.col-resize-container .icon');
Array.prototype.push.apply(domElmIconList, domElemIcons);
domElmIconList.forEach((el) => {
const marginTopNumber = (theadHight - iconHeight) / 2;
el.style.marginTop = `${tryParseInt(marginTopNumber)}px`;
});
}
onGripMouseDown(e) {
e.preventDefault();
const { index } = e.currentTarget;
const domElmHandle = this.domElmHandleList[index];
addClass(domElmHandle, 'active-drag');
domElmHandle.initPageLeftX = e.pageX;
domElmHandle.initLeft = tryParseInt(domElmHandle.style.left);
domElmHandle.x = domElmHandle.initLeft;
this.drag = domElmHandle;
document.addEventListener('mousemove', this.onMouseMove);
document.addEventListener('mouseup', this.onMouseUp);
return false;
}
onMouseMove(e) {
e.preventDefault();
if (!this.drag) {
return false;
}
const { defaultMinWidth } = this.options;
const index = this.drag.index;
const minWidth = defaultMinWidth;
const pageLeftX = e.pageX;
let x = (pageLeftX - this.drag.initPageLeftX) + this.drag.initLeft;
const l = (this.cellSpacing * 1.5) + minWidth + this.borderLeftWidth;
const min = index ? tryParseInt(this.domElmHandleList[index - 1].style.left)
+ this.cellSpacing + minWidth : l;
const max = tryParseInt(this.domElmHandleList[index + 1].style.left)
- this.cellSpacing - minWidth;
x = Math.max(min, Math.min(max, x));
const inc = x - this.drag.initLeft;
const domElmThNow = this.domElmTableTheadThList[index];
const domElmThElmNext = this.domElmTableTheadThList[index + 1];
const w = domElmThNow.w + inc;
const w2 = domElmThElmNext.w - inc;
const minWidthOne = tryParseInt(this.domElmTableTheadThList[index].getAttribute('data-min-width'));
const minWidthTwo = tryParseInt(this.domElmTableTheadThList[index + 1].getAttribute('data-min-width'));
if (minWidthOne > w) {
x = (minWidthOne - domElmThNow.w) + this.drag.initLeft;
} else if (minWidthTwo > w2) {
x = (domElmThElmNext.w - minWidthTwo) + this.drag.initLeft;
}
this.drag.x = x;
this.drag.style.left = `${x}px`;
if (this.options.liveDrag) {
this.syncCols(index);
this.syncGrips();
const { onResizing } = this.options;
if (isFunction(onResizing)) {
onResizing(e);
}
}
return false;
}
syncCols(i, isOver) {
const inc = this.drag.x - this.drag.initLeft;
const domElmThNow = this.domElmTableTheadThList[i];
const domElmThNext = this.domElmTableTheadThList[i + 1];
const w = domElmThNow.w + inc;
const w2 = domElmThNext.w - inc;
domElmThNow.style.width = `${w}px`;
domElmThNext.style.width = `${w2}px`;
if (isOver) {
domElmThNow.w = w;
domElmThNext.w = w2;
}
}
onMouseUp(e) {
document.removeEventListener('mouseup', this.onMouseUp);
document.removeEventListener('mousemove', this.onMouseMove);
if (!this.drag) {
return false;
}
removeClass(this.drag, 'active-drag');
if (!(this.drag.x - this.drag.initLeft === 0)) {
const index = this.drag.index;
this.syncCols(index, true);
this.syncGrips();
const { onResized } = this.options;
if (isFunction(onResized)) {
onResized(e);
}
}
this.drag = null;
return true;
}
}
utils.js
import parseInt from 'lodash/parseInt';
export const tryParseInt = (value, defaultValue = 0) => {
const resultValue = parseInt(value);
if (isNaN(resultValue)) {
return defaultValue;
}
return resultValue;
};
export function addClass(elm, className) {
if (!className) return;
const els = Array.isArray(elm) ? elm : [elm];
els.forEach((el) => {
if (el.classList) {
el.classList.add(className.split(' '));
} else {
el.className += ` ${className}`;
}
});
}
export function removeClass(elm, className) {
if (!className) return;
const els = Array.isArray(elm) ? elm : [elm];
els.forEach((el) => {
if (el.classList) {
el.classList.remove(className.split(' '));
} else {
el.className = el.className.replace(new RegExp(`(^|\\b)${className.split(' ').join('|')}(\\b|$)`, 'gi'), ' ');
}
});
}
index.less
import parseInt from 'lodash/parseInt';
export const tryParseInt = (value, defaultValue = 0) => {
const resultValue = parseInt(value);
if (isNaN(resultValue)) {
return defaultValue;
}
return resultValue;
};
export function addClass(elm, className) {
if (!className) return;
const els = Array.isArray(elm) ? elm : [elm];
els.forEach((el) => {
if (el.classList) {
el.classList.add(className.split(' '));
} else {
el.className += ` ${className}`;
}
});
}
export function removeClass(elm, className) {
if (!className) return;
const els = Array.isArray(elm) ? elm : [elm];
els.forEach((el) => {
if (el.classList) {
el.classList.remove(className.split(' '));
} else {
el.className = el.className.replace(new RegExp(`(^|\\b)${className.split(' ').join('|')}(\\b|$)`, 'gi'), ' ');
}
});
}
网友评论