前言:
此刻姐妹们对“html table 固定列宽”大约比较注重,各位老铁们都想要分析一些“html table 固定列宽”的相关内容。那么小编也在网摘上搜集了一些对于“html table 固定列宽””的相关资讯,希望看官们能喜欢,小伙伴们快快来学习一下吧!大家好,我是前端西瓜哥。
之前因为工作上的一些原因,使用的 wangEditor 富文本编辑器的表格(table)功能并不能满足需求,创建的表格只是设置了 width: 100%,列的宽度的自适应的效果非常不美观。于是决定修改源码,对表格功能做了加强,使其支持调整列宽。
为了方便读者理解,最近把这部分逻辑单独抽离出来并做了优化,写了个能独立运行的 demo,配合这篇文章简单说一说实现思路。
实现思路1. 创建空表格
第一步是创建空表格。要想支持表格可以设置宽度,需要在 table 元素下使用 colgroup 元素。我们根据表格列的数量,在 colgroup 下创建同等数量的 col 元素并设置宽度,就能给对应顺序的列赋予宽度。效果可以查看这个 在线demo 。
此外,表格的单元格如果没有内容,是会发生高度塌陷的,且无法通过 min-height 和 height 设置高度 。为解决这个问题,我们通常会在创建空表格时,给单元格添加 <br /> 内容。
2. 高亮线的显现
第二步是实现光标划到列之间的分割线上方时,将高亮线显现出来。实现方式是:
初始化时,在编辑器容器元素下创建一个绝对定位的 div 元素作为高亮线,并隐藏。给编辑器容器元素绑定鼠标移动事件。当光标移动到表格元素内部时,遍历 col 元素得到所有列分隔线的位置(最左边的不需要)。若光标位于这些分隔线附近,拿到左位移量,然后修改高亮线的位置并将其显现出来;否则隐藏高亮线。3. 拖拽高亮线实现设置列宽
前面提到,当光标悬浮在列分割线上方附近,会出现高亮线。其实为实现修改列宽,我们还要记录一些数据:(1)对应的表格元素;(2)第几列 rowIdx。当然光标移开时,要把这些数据清空。
下面是拖拽这一动作的实现,涉及到鼠标按下、鼠标移动、鼠标释放三个事件:
当鼠标在高亮线上 按下 时,我们要将上面的数据拷贝一份,然后再记录好要修改的(3)列的宽度;(4)光标左偏移量 left(后面要用来计算相对移动位置)(5)对应的 col 元素。在鼠标按下不释放的情况下,移动鼠标,就代表着 拖拽 这一动作的发生。此时,我们计算光标的相对左移动,给对应的 col 元素设置宽度,就能实现动图上的效果。鼠标释放,将这些数据重置为空。
因为只需要得到拖拽的相对位置,所以我们不是必须要将事件绑定到编辑器容器上。另外为了确保鼠标释放事件正在发生,最终我选择在 document 元素上绑定事件。document 元素上绑定的鼠标释放事件,按下鼠标后,即便在浏览器外面释放鼠标,也能触发。
另外,为处理列宽过小,甚至计算出负值的情况,我们需要设置一个最小列宽。
代码实现
那么这里就简单讲解一下核心代码。
首先是创建空表格:
const DEFAULT_COL_WIDTH = 80const DEFAULT_COL_SIZE = 5const DEFAULT_ROW_SIZE = 5// 生成空 tablefunction createTable(row = DEFAULT_ROW_SIZE, col = DEFAULT_COL_SIZE, hasTh = false, colWidth = DEFAULT_COL_WIDTH) { const tbody = document.createElement('tbody') let tr, td for (let i = 0; i < row; i++) { tr = document.createElement('tr') tbody.appendChild(tr) for (let j = 0; j < col; j++) { td = document.createElement((i === 0 && hasTh) ? 'th' : 'td') td.innerHTML = '<br />' // 填充点东西,处理单元格高度塌陷问题 tr.appendChild(td) } } const colgroup = document.createElement('colgroup') let colElm for (let i = 0; i < col; i++) { colElm = document.createElement('col') colElm.style.width = colWidth + 'px' colgroup.appendChild(colElm) } const table = document.createElement('table') table.appendChild(colgroup) table.appendChild(tbody) return table}
显现高亮线的核心实现逻辑如下。计算左偏移量使用了 getBoundingClientRect() 方法和鼠标事件中得到的 clientX 坐标。其中还需要注意坐标系有 “相对表格” 和 “相对编辑器容器” 两种。
const hl = new HL(editorEl) // 高亮线对象// ...let lastState = {} // 高亮线出现时,记录对应的 tableEl 和 rowIdxfunction handleMovemove(event) { // 找到最近的table父元素 const nearestTable = getNearestTable(event.target, editorEl) if (!nearestTable) { hl.hideHL() lastState = {} return } const tableLeftByViewport = nearestTable.getBoundingClientRect().left const cursorLeftByTable = event.clientX - tableLeftByViewport const tableLeftByEditor = tableLeftByViewport - editorEl.getBoundingClientRect().left //accurateLeft 为精准的列分割线上的左偏移量 const { idx: rowIdx, left: accurateLeft } = getNearestRowBorder(nearestTable, cursorLeftByTable) if (rowIdx !== -1) { hl.drawHL(nearestTable, tableLeftByEditor + accurateLeft) // 转换为相对 editor 容器的左偏移量 lastState = { tableEl: nearestTable, rowIdx } // 保存 table 元素和 列索引,鼠标按下时要用到。 editorEl.style.cursor = 'col-resize' } else { hl.hideHL() lastState = {} }}
然后是拖拽的逻辑。dragState.tableEl 除了记录保存当前表格元素,还在 moveover 事件中做是否触发拖拽的标识。
// 拖拽过程中保存的变量const emptyDragState = { tableEl: undefined, rowIdx: undefined, col: null, width: undefined, left: undefined,}let dragState = {...emptyDragState}function dragMouseDown(event) { // TODO: 需要点左键才执行 const { tableEl, rowIdx } = lastState if (!tableEl) return const col = getCol(tableEl, rowIdx) const width = parseFloat(col.style.width) const left = event.clientX dragState = { ...dragState, ...lastState, col, width, left }}function dragMouseover(event) { const { left, tableEl, width, rowIdx } = dragState if (!tableEl) return const offset = event.clientX - left setColWidth(tableEl, rowIdx, width + offset)}function dragMouseup() { dragState = {...emptyDragState}}结尾
俗话说的好,“Talk is cheap, show me your code!”。如果你对里面的实现细节感兴趣的话,请去我的在线 demo 阅读源码和测试:
标签: #html table 固定列宽