当前位置: 首页 > Web前端 > HTML

表头可拖拽的实现

时间:2023-04-02 20:50:44 HTML

前言我做的项目遇到这样一个需求,就是给所有的表添加表头可以拖拽的效果。我想,这并不容易,我会在几分钟内为您完成。拿起我的电脑,它开始敲门。一定有问题。以我的聪明才智,结果不应该是这样的。然后静下心来整理思绪,终于做出来了。至于结果,我肯定是做出来了,像下面这样:首先要说明的是,我们项目中使用的表格大致分为两种,一种是表头不固定,就是普通的表格,另一种是header是固定的,tbody部分是可滚动的。需要注意的是,固定表头的需要用两张表来实现,做过的应该都懂。前者看起来比较简单,因为宽度受thead中th的影响,后者看起来比较难处理,因为如果用两个表,会出现下面的情况:emmm,这应该和我们想象的不一样,怎么样这个能修好吗,感觉处理起来很麻烦。记得在element-ui里看到过table,好像有拖拽tableheader的实现,先打开console看看结构:呃,我从来没用过和这两个标签好久,不过仔细观察上面有个宽度,看到这个你大概就知道是怎么回事了。打开MDN,阅读相关属性的描述。如你所想,width可以控制当前列的宽度。宽度控制我们已经解决了,还有一个问题,就是拖动后其他列的宽度如何改变,如下:,我是这样处理的。b、c、d都有属性表示该列是否被拖动。如果b,c,d都没有被拖过,那么把a改变的宽度分成b,c,d三列的宽度上,如果b,c,d都改变了,那么只有最后一列d将被更改。好吧,想法已经有了,我们可以实现它。结果发现按照上面的设计就太傻了,改成了只改变被拖列后面的列,这些列的宽度没有改变。首先,html结构大概是这样的:

ab
12
js方面构造函数(id,选项){this._el=document.querySelector(`#${id}`);//在实际使用中,需要判断dom结构,所以this._tables=Array.from(this._el.querySelectorAll('table'));setTimeout(()=>this._resolveDom());this._tables=Array.from(this._el.querySelectorAll('table'));store={dragging:false,//是否拖动draggingColumn:null,//拖动对象miniWidth:30,//拖动的最小宽度startMouseLeft:undefined,//鼠标点击时的clientXstartLeft:undefined,//ThedistancebetweenthrightandtablestartColumnLeft:undefined,//第thleft和table之间的距离tableLeft:undefined,//table和页面左侧的距离,HColumns:[],BColumns:[],};};添加dom:const[THeader]=this._tables;letTBody;constTr=THeader.tHead.rows[0];constcolumns=Array.from(Tr.cells);constBcolgroup=document.createElement('colgroup');constcols=columns.map((项目,索引)=>{constcol=document.createElement('col');item.dataset.index=index;col.width=+item.offsetWidth;returncol;});cols.reduce((newDom,item)=>{newDom.appendChild(item);returnnewDom;},Bcolgroup);constHColgroup=Bcolgroup.cloneNode(true);THeader.appendChild(HColgroup);//是否是一个一两张表,提出表头和表体if(this._tables.length===1){const[,tbody]=Array.from(THeader.children);tbody.remove();TBody=THeader.cloneNode();TBody.appendChild(Bcolgroup);身体。附加子(tbody);this._el.appendChild(TBody);}else{[,TBody]=this._tables;TBody.appendChild(Bcolgroup);}//拖动时的占位线consthold=document.createElement('div');hold.classList.add('resizable-hold');this._el.appendChild(hold);上面一块是添加节点,处理dom,为了复用,这里我们不管你是一张表,表头是固定的还是表头不固定的,我们拆分成两个表,这样也方便很多,然后将光标的值设置为col-resize:handleMouseMove(evt){//...if(!this.store.dragging){constrect=target.getBoundingClientRect();constbodyStyle=document.body.style;如果(rect.width>12&&rect.right-event.pageX<8){bodyStyle.cursor='col-resize';target.style.cursor='col-resize';this.store.draggingColumn=目标;}else{bodyStyle.cursor='';target.style.cursor='指针';this.store.draggingColumn=null;}}};需要注意的是,getBoundingClientRect()得到的rigth是元素右侧到页面左边缘的距离,而不是页面右边缘的距离。这里就是给thead的tr添加mousemove事件。当鼠标指针距离右边缘小于8时,改变指针的形状,然后改变store中的状态,说明此时点击可以拖动。然后就是mousedown+mousemove+mouseup来处理拖动了:consthandleMouseDown=(evt)=>{if(this.store.draggingColumn){this.store.dragging=true;让{目标}=evt;如果(!目标)返回;consttableEle=THeader;consttableLeft=tableEle.getBoundingClientRect().left;constcolumnRect=target.getBoundingClientRect();constminLeft=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;文档.onselectstart=()=>false;文档.ondragstart=()=>false;hold.style.display='方块';hold.style.left=this.store.startLeft+'px';consthandleOnMouseMove=(event)=>{constdeltaLeft=event.clientX-this.store.startMouseLeft;constproxyLeft=this.store.startLeft+deltaLeft;hold.style.left=Math.max(minLeft,proxyLeft)+'px';};//宽度是这样分配的,比如?,如果a,b,c,d,它们每个都有变化的状态,默认为false,拖过a,a.changed为true,变化的宽度就是剩下的b,c,d共享,如果都变了,就让最后一个元素d来承担责任;constfinalLeft=parseInt(hold.style.left,10);constcolumnWidth=finalLeft-startColumnLeft;constindex=+target.dataset。指数;HColgroup.children[index].width=columnWidth;if(index!==this.store.HColumns.length-1){this.store.HColumns[index].isChange=true;constdeltaLeft=event.clientX-this.store.startMouseLeft;constchangeColumns=this.store.HColumns.filter(v=>!v.isChange&&+v.el.width>30);changeColumns.forEach(item=>{item.el.width=+item.el.width-deltaLeft/changeColumns.length;});this.store.BColumns.forEach((item,i)=>{item.el.width=this.store.HColumns[i].el.width;});//...初始化存储}document.removeEventListener('mousemove',handleOnMouseMove);document.removeEventListener('mouseup',handleOnMouseUp);文档.onselectstart=null;文档.ondragstart=null;//noclick主要用于判断是点击还是拖动,防止拖动触发排序setTimeout(()=>{target.classList.remove('noclick');},0);};document.addEventListener('mouseup',handleOnMouseUp);document.addEventListener('mousemove',handleOnMouseMove);}};Tr.addEventListener('mousedown',handleMouseDown);预览效果(chrome+Safari+Firefox)总结起来很有趣也很有用,也让我养了很多姿势。源码已经做成了类的形式,使用起来还是挺简单的,因为是突然提出来的,我没有做过太多的测试,可能会有不知道的bug。祝福写在最后。马上就要过年了,心情还是很愉快的。所以,我在这里提前祝大家新年快乐,,,,过段时间开心一下,嘿嘿嘿。Bye~后面的补充改了改变宽度的方式,应该只改变被拖列后面的列的宽度。有个BUG,colgroup放在head下面,导致safari下有BUG,已经修复了,没仔细看,但是上面的代码没改。看代码还是得看源码。我没有发现这个问题。其他人帮我找出来。emmmmm,我又发现了一个问题,就是拖到最后一列的时候。..我想了想,先睡了==