您好,欢迎访问一九零五行业门户网

如何实现可拖动table表头

首先要说明的是,我们的项目使用的表格大概只分为两类,一类是表头不固定,就是普通的表格,另一类是表头固定,tbody部分是可以滚动的。需要说明的是,表头固定的那种是需要用两个table去实现,做过的人应该也都明白。前者看起来比较简单,因为宽度是受thead里的th影响的,后者看起来就不好处理,因为你用两个table就会出现下面的情况:
emmm,这和我们想象的应该不一样,这可咋整,感觉处理起来很麻烦啊。想起看过element-ui中的表格,似乎有拖动表头的实现,先打开控制台看下结构吧:
呃,话说长这么大我都没用过<colgroup>和<col>这两个标签,但仔细观察上面有个width,看到这大概也知道是怎么回事了,打开mdn看下相关属性的描述,和想的一样,width能控制当前列的宽度。
宽度的控制我们是解决了,还有一个问题,就是拖动后,其他列的宽度改怎么改变,如下:
abcd
如果我拖动a列,改变的宽度应该怎样分配到b,c,d上,我这里是这样处理的,b、c、d有个属性去表示该列是否已经被拖动过了,如果b、c、d都没拖动过,那么把a改变的宽度平分到b、c、d三列的宽度上,如果b、c、d都改变了话,那么只改变最后一列d的宽度。好了,思路已经有了,我们可以去实现了。
事实证明,如果按照上面的设计就太蠢了,已经改成只改变拖动列后面的列且这些列没有改变过宽度。
实现首先html结构大概是这样的:
<table>   <thead>     <tr>       <th>a<th>       <th>b<th>     </tr>   </thead>   <tbody>     <tr>       <th>1<th>       <th>2<th>     </tr>   </tbody> </table>
js方面
  constructor (id, options) {     this._el = document.queryselector(`#${id}`);     // 实际使用中需要对dom结构进行判断,这里就不做了     this._tables = array.from(this._el.queryselectorall('table'));     settimeout(() => this._resolvedom());     this.store = {       dragging: false,                 //是否拖动       draggingcolumn: null,            //拖动的对象       miniwidth: 30,                   //拖动的最小宽度       startmouseleft: undefined,       //鼠标点击时的clientx       startleft: undefined,            //th右离table的距离       startcolumnleft: undefined,      //th左离table的距离       tableleft: undefined,            //table离页面左边的距离,       hcolumns: [],       bcolumns: [],     };   };
添加dom:
const [ theader ] = this._tables; let tbody; const tr = theader.thead.rows[0]; const columns = array.from(tr.cells); const bcolgroup = document.createelement('colgroup'); const cols = columns.map((item, index) => {   const col = document.createelement('col');   item.dataset.index = index;   col.width = +item.offsetwidth;   return col; }); cols.reduce((newdom, item) => {   newdom.appendchild(item);   return newdom; }, bcolgroup); const hcolgroup = bcolgroup.clonenode(true); theader.appendchild(hcolgroup); //不管是一个table还是两个,都把header和body提出来 if (this._tables.length === 1) {   const [ , tbody ] = array.from(theader.children);   tbody.remove();   tbody = theader.clonenode();   tbody.appendchild(bcolgroup);   tbody.appendchild(tbody);   this._el.appendchild(tbody); } else {   [ , tbody ] = this._tables;   tbody.appendchild(bcolgroup); } //拖动时的占位线 const hold = document.createelement('p'); hold.classlist.add('resizable-hold'); this._el.appendchild(hold);
上面这块就是添加节点的,对dom进行处理,为了复用,这里我们不管你是表头固定还是表头不固定,我们都拆分为两个table,这样处理起来也方便的多。
然后就是处理手指移到列右侧cursor的值设为col-resize:
handlemousemove(evt) {   //...   if (!this.store.dragging) {     const rect = target.getboundingclientrect();     const bodystyle = document.body.style;     if (rect.width > 12 && rect.right - event.pagex < 8) { bodystyle.cursor = 'col-resize'; target.style.cursor = 'col-resize'; this.store.draggingcolumn = target; } else { bodystyle.cursor = ''; target.style.cursor = 'pointer'; this.store.draggingcolumn = null; } } };
需要注意的是,getboundingclientrect()获取的rigth是元素右侧距离页面左边缘的距离,不是离页面右边缘的距离。这里就是给thead的tr添加mousemove事件,当鼠标指针距离右边缘小于8的时候,改变指针形状,然后改变store里的状态,表示此时点击是可以拖动的了。
然后就是mousedown+mousemove+mouseup来处理拖动了:
const handlemousedown = (evt) => {   if (this.store.draggingcolumn) {     this.store.dragging = true;     let { target } = evt;     if (!target) return;     const tableele = theader;     const tableleft = tableele.getboundingclientrect().left;     const columnrect = target.getboundingclientrect();     const minleft = columnrect.left - tableleft + 30;     target.classlist.add('noclick');     this.store.startmouseleft = evt.clientx;     this.store.startleft = columnrect.right - tableleft;     this.store.startcolumnleft = columnrect.left - tableleft;     this.store.tableleft = tableleft;     document.onselectstart = () => false;     document.ondragstart = () => false;     hold.style.display = 'block';     hold.style.left = this.store.startleft + 'px';     const handleonmousemove = (event) => {       const deltaleft = event.clientx - this.store.startmouseleft;       const proxyleft = this.store.startleft + deltaleft;       hold.style.left = math.max(minleft, proxyleft) + 'px';     };     // 宽度是这样分配的,举个
以上就是如何实现可拖动table表头的详细内容。
其它类似信息

推荐信息