在现代 Web 应用中,表格是一种常见的数据展示方式。然而,默认的表格列宽往往不能满足用户的实际需求。本文将介绍如何使用 HTML、CSS 和 JavaScript 实现一个支持列宽调整的交互式表格,让用户能够根据自己的需要自由调整每一列的宽度。效果演示
这个可调整列宽的表格具有直观的操作体验。当鼠标悬停在表头(th)上时,会在右侧显示出一个可拖动的调整手柄。用户只需按下鼠标左键并向左右拖动,就能实时改变对应列的宽度。这种交互方式使得表格更加灵活易用,特别适用于需要查看长文本内容或者希望优化空间利用的场景。
页面结构
页面通过 .wrap 区域实现了横向滚动条的支持,防止表格溢出屏幕。使用 table-layout: fixed 来固定表格布局,使我们可以控制每列的具体宽度。在每个 th 元素的右边添加了一个伪元素 ::after 作为调整手柄,只有当鼠标悬停时才可见。<div class="wrap"> <table class="resizable-table" id="resizableTable"> <thead> <tr> <th style="width: 80px">姓名</th> <th>年龄</th> <th>城市</th> <th style="width: 150px">邮箱</th> <th>电话</th> </tr> </thead> <tbody> <tr> <td>张三</td> <td>25</td> <td>北京</td> <td>zhangsan@example.com</td> <td>13800138000</td> </tr> </tbody> </table></div>
核心功能实现
初始化列宽
页面加载完成后会执行 initColumnWidths() 函数来初始化各列的宽度。该函数首先获取表格总宽度,然后遍历所有表头单元格。如果某列已经设置了具体宽度,则将其记录下来;对于没有指定宽度的列,则平均分配剩余的空间;最后调用 updateTableWidth() 应用这些初始宽度值。function initColumnWidths() { tableWidth = table.offsetWidth; var remainingWidth = tableWidth - 1; var unsetColumns = []; headers.forEach((th, index) => { if (th.style.width) { var width = parseInt(th.style.width); columnWidths[index] = Math.max(50, width); remainingWidth -= columnWidths[index]; } else { unsetColumns.push(index); } }); if (unsetColumns.length > 0) { var avgWidth = Math.max(50, remainingWidth / unsetColumns.length); unsetColumns.forEach(index => { columnWidths[index] = avgWidth; }); } updateTableWidth();}
鼠标事件处理
为了响应用户的拖拽行为,我们对每个 th 元素绑定了 mousedown 事件监听器。当检测到点击发生在靠近右边缘的位置时(即调整区域内),就会启动拖拽流程。headers.forEach((th, index) => { th.addEventListener('mousedown', function (e) { var rect = th.getBoundingClientRect(); if (e.clientX < rect.right - 6) return; e.preventDefault(); currentResizer = th; startX = e.clientX; columnIndex = index; startWidth = th.offsetWidth; th.style.opacity = '1'; document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleMouseUp); });});
动态更新表格尺寸
每当列宽发生变化时,都会触发 updateTableWidth() 方法重新计算并应用最新的尺寸信息。function updateTableWidth() { var totalWidth = columnWidths.reduce((sum, width) => sum + width, 0); table.style.width = totalWidth + 'px'; var headers = table.querySelectorAll('th'); headers.forEach((th, index) => { th.style.width = columnWidths[index] + 'px'; }); var rows = table.querySelectorAll('tbody tr'); rows.forEach(row => { var cells = row.querySelectorAll('td'); cells.forEach((td, index) => { td.style.width = columnWidths[index] + 'px'; }); });}
扩展建议
完整代码
git地址:https://gitee.com/ironpro/hjdemo/blob/master/table-resizable/index.html<!DOCTYPE html><html lang="zh-CN"><head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>可调整列宽的表格</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { background: #f5f5f5; min-height: 100vh; padding: 20px; } .container { max-width: 600px; margin: 0 auto; background: white; border-radius: 15px; box-shadow: 0 20px 40px rgba(0,0,0,0.1); overflow: hidden; } .header { background: #007bff; color: white; padding: 20px; text-align: center; } .header h1 { font-size: 24px; font-weight: 500; } .main { padding: 30px; min-height: 300px; } .wrap { overflow-x: auto; width: 100%; min-width: 0; } table { table-layout: fixed; border-collapse: collapse; width: 100%; } th, td { border: 1px solid #999; padding: 6px 8px; } th { position: relative; } th::after { content: ''; position: absolute; top: 0; right: 0; width: 5px; height: 100%; cursor: col-resize; background-color: #999; opacity: 0; transition: opacity 0.2s ease; } th:hover::after { opacity: 1; } th, td { border: 1px solid #999; padding: 6px 8px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; box-sizing: border-box; } </style></head><body><div class="container"> <div class="header"> <h1>可调整列宽的表格</h1> </div> <div class="main"> <div class="wrap"> <table class="resizable-table" id="resizableTable"> <thead> <tr> <th style="width: 80px">姓名</th> <th>年龄</th> <th>城市</th> <th style="width: 150px">邮箱</th> <th>电话</th> </tr> </thead> <tbody> <tr> <td>张三</td> <td>25</td> <td>北京</td> <td>zhangsan@example.com</td> <td>13800138000</td> </tr> <tr> <td>李四</td> <td>30</td> <td>上海</td> <td>lisi@example.com</td> <td>13900139000</td> </tr> <tr> <td>王五</td> <td>28</td> <td>广州</td> <td>wangwu@example.com</td> <td>13700137000</td> </tr> </tbody> </table> </div> </div></div>
<script> var table = document.getElementById('resizableTable'); var headers = table.querySelectorAll('thead th'); var columnCount = headers.length; var columnWidths = new Array(columnCount);
var currentResizer = null; var startX = 0; var startWidth = 0; var columnIndex = -1; var tableWidth = 0;
function initColumnWidths() { tableWidth = table.offsetWidth; var remainingWidth = tableWidth - 1; var unsetColumns = []; headers.forEach((th, index) => { if (th.style.width) { var width = parseInt(th.style.width); columnWidths[index] = Math.max(50, width); remainingWidth -= columnWidths[index]; } else { unsetColumns.push(index); } }); if (unsetColumns.length > 0) { var avgWidth = Math.max(50, remainingWidth / unsetColumns.length); unsetColumns.forEach(index => { columnWidths[index] = avgWidth; }); } updateTableWidth(); }
headers.forEach((th, index) => { th.addEventListener('mousedown', function (e) { var rect = th.getBoundingClientRect(); if (e.clientX < rect.right - 6) return; e.preventDefault(); currentResizer = th; startX = e.clientX; columnIndex = index; startWidth = th.offsetWidth; th.style.opacity = '1'; document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleMouseUp); }); });
function handleMouseMove(e) { if (!currentResizer) return; var diff = e.clientX - startX; columnWidths[columnIndex] = Math.max(50, startWidth + diff); updateTableWidth(); }
function handleMouseUp() { document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mouseup', handleMouseUp); currentResizer = null; columnIndex = -1; }
function updateTableWidth() { var totalWidth = columnWidths.reduce((sum, width) => sum + width, 0); table.style.width = totalWidth + 'px'; var headers = table.querySelectorAll('th'); headers.forEach((th, index) => { th.style.width = columnWidths[index] + 'px'; }); var rows = table.querySelectorAll('tbody tr'); rows.forEach(row => { var cells = row.querySelectorAll('td'); cells.forEach((td, index) => { td.style.width = columnWidths[index] + 'px'; }); }); } initColumnWidths();</script></body></html>
阅读原文:https://mp.weixin.qq.com/s/44uHl-l535g-WKUmC1A_Gw
该文章在 2025/12/18 9:28:57 编辑过