拖拽列宽时只修改 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 就行。否则容易误触发或漏触发。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,像素值才稳定table.onmouseup 根本不会触发,状态残留导致后续拖拽失效或宽度错乱。mousedown 时立刻绑定 document.onmouseup 和 document.onmousemove
mouseup 回调里必须:document.onmouseup = nulldocument.onmousemove = nullremoveEventListener,避免重复绑定)table.style.width,否则总宽失衡会让其他列被挤压或撑开真实场景里最易被忽略的点:拖拽过程中没同步更新同列所有 td 的 style.width,结果表头变宽、内容列还卡在原宽度,视觉上直接错位。这不是 bug,是忘了做这件事。