返回

进阶掌握:解密通用表格合并单元格的极佳极简方案

前端

在日复一日的程序员工作中,表格的身影随处可见,合并单元格似乎更是一个避不开的难题。从前都是临时抱佛脚,遇到一个表格就针对性地解决一次,这确实很繁琐。于是,作为一个颇具匠心的开发者,我决定一劳永逸,亲手打造一款极佳极简的通用表格合并单元格方案。

过去几年我经手的表格形形色色,有的表格仅仅几行几列,有的却如同数据库查询结果一般动辄几十上百行。这么多年来,表格始终伴随左右,而作为程序员,自然离不开合并单元格。

合并单元格,在程序员的眼中,那绝对是一门艺术。网上也存在不少的解决方案,有涉及JavaScript 的,也有涉及jQuery 的,更有纯CSS 的。然而,它们的共同点是,对于普通的开发者来说,学习和使用成本偏高,因此最终没有被广泛推广开来。

完美主义者的全新方案

既然现有方案都不尽人意,那就自己动手吧。我决定亲手打造一个通用、极简的表格合并单元格方案,它的特点是:

  1. 简单易用
  2. 性能卓越
  3. 跨浏览器
  4. 跨平台

该方案仅依赖JavaScriptCSS ,无需任何第三方库。开发者只需要引入这两个文件,即可在任何网页中使用该方案。

精妙的实现思路

那么,这个方案的实现思路是什么呢?

  1. 首先,我们对表格进行网格布局。
  2. 其次,我们利用JavaScript 来监听表格的事件,如鼠标点击、鼠标拖拽等。
  3. 当用户触发这些事件时,我们就通过JavaScript 来控制单元格的合并和拆分。
  4. 最后,我们利用CSS 来控制合并单元格的样式。

炉火纯青的编码实践

下面,我们就来看看如何实现这个方案吧。

  1. 首先,我们在HTML 中创建一个表格,并为其添加网格布局。
<table class="grid">
  <thead>
    <tr>
      <th>姓名</th>
      <th>年龄</th>
      <th>性别</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>张三</td>
      <td>20</td>
      <td>男</td>
    </tr>
    <tr>
      <td>李四</td>
      <td>25</td>
      <td>女</td>
    </tr>
    <tr>
      <td>王五</td>
      <td>30</td>
      <td>男</td>
    </tr>
  </tbody>
</table>
  1. 其次,我们在JavaScript 中监听表格的事件。
const grid = document.querySelector('.grid');

grid.addEventListener('click', function(e) {
  const target = e.target;

  if (target.tagName === 'TH') {
    // 单击表头时,选中该列的所有单元格
    const column = target.cellIndex;
    const cells = grid.querySelectorAll(`td:nth-child(${column + 1})`);

    cells.forEach(cell => {
      cell.classList.add('selected');
    });
  } else if (target.tagName === 'TD') {
    // 单击单元格时,选中该单元格
    target.classList.add('selected');
  }
});

grid.addEventListener('mousedown', function(e) {
  const target = e.target;

  if (target.tagName === 'TH' || target.tagName === 'TD') {
    // 按下鼠标时,记录起始单元格
    const startCell = target;

    // 监听鼠标移动事件
    document.addEventListener('mousemove', function(e) {
      // 计算鼠标移动的距离
      const deltaX = e.clientX - startCell.getBoundingClientRect().left;
      const deltaY = e.clientY - startCell.getBoundingClientRect().top;

      // 根据鼠标移动的距离,调整选中的单元格
      const cells = grid.querySelectorAll('.selected');

      cells.forEach(cell => {
        const cellRect = cell.getBoundingClientRect();
        const left = cellRect.left + deltaX;
        const top = cellRect.top + deltaY;
        const width = cellRect.width;
        const height = cellRect.height;

        cell.style.left = `${left}px`;
        cell.style.top = `${top}px`;
        cell.style.width = `${width}px`;
        cell.style.height = `${height}px`;
      });
    });

    // 监听鼠标松开事件
    document.addEventListener('mouseup', function() {
      // 松开鼠标时,停止监听鼠标移动事件
      document.removeEventListener('mousemove');

      // 合并选中的单元格
      const cells = grid.querySelectorAll('.selected');

      const startCellRect = startCell.getBoundingClientRect();
      const endCellRect = cells[cells.length - 1].getBoundingClientRect();

      const left = Math.min(startCellRect.left, endCellRect.left);
      const top = Math.min(startCellRect.top, endCellRect.top);
      const width = Math.max(startCellRect.right, endCellRect.right) - left;
      const height = Math.max(startCellRect.bottom, endCellRect.bottom) - top;

      const mergedCell = document.createElement('td');
      mergedCell.style.left = `${left}px`;
      mergedCell.style.top = `${top}px`;
      mergedCell.style.width = `${width}px`;
      mergedCell.style.height = `${height}px`;
      mergedCell.colSpan = cells.length;

      startCell.parentNode.replaceChild(mergedCell, startCell);

      // 移除选中的样式
      cells.forEach(cell => {
        cell.classList.remove('selected');
      });
    });
  }
});
  1. 最后,我们在CSS 中控制合并单元格的样式。
.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-gap: 10px;
}

.grid td {
  border: 1px solid #ccc;
  padding: 5px;
}

.grid .selected {
  background-color: #999;
}

.grid .merged-cell {
  background-color: #666;
  color: #fff;
  font-weight: bold;
  text-align: center;
}

结语

现在,您已经掌握了通用表格合并单元格的极佳极简方案。您只需引入JavaScriptCSS 文件,即可在任何网页中使用该方案。相信我,它一定会让您的工作更加轻松。