拖拽列宽时只修改 th.style.width 并同步更新同列所有 td 的 style.width,而非 <col> 的 width 属性;需基于 getBoundingClientRect() 计算边缘坐标触发,绑定 document 级 mouseup 防止状态残留。

拖拽时只改 th.style.width,别碰 <col> 的 width 属性
<col> 的 width 属性在 table-layout: fixed 下完全无效,改了也白改。浏览器渲染时真正起作用的是 th 和同列所有 td 的 style.width。很多开发者拖完发现没变化,就是因为只更新了 <col width="200">,却没动单元格样式。
- 拖拽开始前,从
th.style.width或getComputedStyle(th).width读取当前宽度(优先用后者,兼容初始无内联样式的场景) - 拖拽中实时设
th.style.width = newWidth + 'px',并遍历该列所有td同步设置 - 拖拽结束再把
newWidth写回<col>的width属性,仅作数据持久化或服务端同步用
判断拖拽区域必须靠坐标计算,不能依赖 hover 或 class
鼠标是否落在列右边缘 5px 内,得靠实时坐标算,不是加个:hover 就行。否则容易误触发或漏触发。
- 对每个
th绑定onmousedown,在回调里算:const rightEdge = th.getBoundingClientRect().left + th.offsetWidthif (Math.abs(event.clientX - rightEdge) <= 5) { /<em> 激活拖拽 </em>/ } - 避免用
event.offsetX—— 在 Firefox 和某些移动端不可靠,统一用getBoundingClientRect()+clientX - 只对
thead tr:first-child th绑定,防止内容行干扰逻辑
table-layout: fixed 是刚需,且需确保第一行 th 有明确宽度
没有 table-layout: fixed,列宽会随内容重排,拖拽效果飘忽不定;但光设这个还不够,第一行单元格必须有可读的宽度值,否则浏览器无法锚定列基准。
- 初始化时强制设
table.style.tableLayout = 'fixed' - 如果表格没内联宽度,先让第一行
th的offsetWidth落地:Array.from(header.cells).forEach(th => th.style.width = th.offsetWidth + 'px') - 别用百分比或
auto值初始化th.style.width,像素值才稳定
松手后列宽“弹回去”?大概率是 mouseup 监听没挂到 document 上
鼠标快速拖出表格区域时,table.onmouseup 根本不会触发,状态残留导致后续拖拽失效或宽度错乱。
-
mousedown时立刻绑定document.onmouseup和document.onmousemove -
mouseup回调里必须:document.onmouseup = nulldocument.onmousemove = null
(或用removeEventListener,避免重复绑定) - 同步重置
table.style.width,否则总宽失衡会让其他列被挤压或撑开
真实场景里最易被忽略的点:拖拽过程中没同步更新同列所有 td 的 style.width,结果表头变宽、内容列还卡在原宽度,视觉上直接错位。这不是 bug,是忘了做这件事。



















