游乐游手机版
首页/前端开发/文章详情

ExtJS 4.2 Grid单元格合并实现技巧

时间:2026-06-12 06:54
ExtJS4 2Grid单元格合并通过操作tr和td的rowspan属性实现,重复行隐藏。多列合并分逐个列与全部列两种方式,提供mergeGrid函数代码,支持指定列索引及是否全部列相同才合并。

ExtJS 4.2 的 Grid 组件默认不具备单元格合并功能,需要开发者手动实现。本文将深入拆解实现原理与核心代码,帮助你快速掌握合并技巧,避免常见问题。

目录

1. 原理
2. 多列合并
3. 代码与在线演示

1. 原理

1.1 HTML 代码分析

首先创建一个基础的 Grid 组件,然后观察其生成的 HTML 结构。

\

\

通过分析生成的 HTML 可以看出,Grid 组件主要分为 grid-headergrid-body 两大区域(若启用了工具栏或分页栏,则会额外拥有独立的区域)。

在 grid-body 内部,包含若干 tr 元素,每个 tr 对应 Grid 中的一行数据;每个 tr 中又包含多个 td,每个 td 就是一个具体的单元格。

\

1.2 原理

步骤说明

整个合并操作的核心都围绕 tr 元素展开,具体流程如下:

  1. 比较第一行 tr 与第二行 tr 的某个 td 的值,若两行的内容相等:将第一行 tr 的该 tdrowspan 属性值增加 1,并将第二行 tr 的对应 td 隐藏。
  2. 接着拿第一行 tr 与第三行 tr 比较,若值仍相等:第一行的 rowspan 再增加 1,同时隐藏第三行的 td
  3. 重复上述步骤,直到遇到值不相等的一行——此时跳过本次比较,转而将当前行作为新的基准行,继续与下一行比较。

示例

1) tr1 与 tr2 比较,值相等:设置 tr1 的 rowspan 加 1,隐藏 tr2 的 td

\

2) tr1 与 tr3 比较,值相等:tr1 的 rowspan 再加 1,隐藏 tr3 的 td

\

3) tr1 与 tr4 比较,值不相等。跳过本次比较,转而用 tr4 与 tr5 比较(当前行与下一行)。

\

2. 多列合并

Grid 的合并可分为单列合并和多列合并。其中多列合并又包含两种模式:

  • 逐个列合并:每个列在前一列合并的前提下,可以独立进行合并操作。
  • 全部列合并:只有当相邻 tr 中指定的所有列内容都相同时,才会执行合并。

2.1 逐个列合并

说明:每个列可基于前面列的合并结果独立判断是否合并。

示例:

\

2.2 全部列合并

说明:只有相邻 tr 中所有指定列的内容完全一致时,才会进行合并。

示例:

\

3. 代码与在线演示

3.1 代码

/**
 * 合并Grid的数据列
 * @param grid {Ext.Grid.Panel} 需要合并的Grid
 * @param colIndexArray {Array} 需要合并列的Index(序号)数组;从0开始计数,序号也包含。
 * @param isAllSome {Boolean} 是否2个tr的colIndexArray必须完成一样才能进行合并。true:完成一样;false:不完全一样
 */
function mergeGrid(grid, colIndexArray, isAllSome) {
 isAllSome = isAllSome == undefined ? true : isAllSome; // 默认为true
 
 // 1.是否含有数据
 var gridView = document.getElementById(grid.getView().getId() + '-body');
 if (gridView == null) {
 return;
 }
 
 // 2.获取Grid的所有tr
 var trArray = [];
 if (grid.layout.type == 'table') { // 若是table部署方式,获取的tr方式如下
 trArray = gridView.childNodes;
 } else {
 trArray = gridView.getElementsByTagName('tr');
 }
 
 // 3.进行合并操作
 if (isAllSome) { // 3.1 全部列合并:只有相邻tr所指定的td都相同才会进行合并
 var lastTr = trArray[0]; // 指向第一行
 // 1)遍历grid的tr,从第二个数据行开始
 for (var i = 1, trLength = trArray.length; i < trLength; i++) {
  var thisTr = trArray[i];
  var isPass = true; // 是否验证通过
  // 2)遍历需要合并的列
  for (var j = 0, colArrayLength = colIndexArray.length; j < colArrayLength; j++) {
  var colIndex = colIndexArray[j];
  // 3)比较2个td的列是否匹配,若不匹配,就把last指向当前列
  if (lastTr.childNodes[colIndex].innerText != thisTr.childNodes[colIndex].innerText) {
   lastTr = thisTr;
   isPass = false;
   break;
  }
  }
 
  // 4)若colIndexArray验证通过,就把当前行合并到'合并行'
  if (isPass) {
  for (var j = 0, colArrayLength = colIndexArray.length; j < colArrayLength; j++) {
   var colIndex = colIndexArray[j];
   // 5)设置合并行的td rowspan属性
   if (lastTr.childNodes[colIndex].hasAttribute('rowspan')) {
   var rowspan = lastTr.childNodes[colIndex].getAttribute('rowspan') - 0;
   rowspan++;
   lastTr.childNodes[colIndex].setAttribute('rowspan', rowspan);
   } else {
   lastTr.childNodes[colIndex].setAttribute('rowspan', '2');
   }
   // lastTr.childNodes[colIndex].style['text-align'] = 'center';; // 水平居中
   lastTr.childNodes[colIndex].style['vertical-align'] = 'middle';; // 纵向居中
   thisTr.childNodes[colIndex].style.display = 'none';
  }
  }
 }
 } else { // 3.2 逐个列合并:每个列在前面列合并的前提下可分别合并
 // 1)遍历列的序号数组
 for (var i = 0, colArrayLength = colIndexArray.length; i < colArrayLength; i++) {
  var colIndex = colIndexArray[i];
  var lastTr = trArray[0]; // 合并tr,默认为第一行数据
  // 2)遍历grid的tr,从第二个数据行开始
  for (var j = 1, trLength = trArray.length; j < trLength; j++) {
  var thisTr = trArray[j];
  // 3)2个tr的td内容一样
  if (lastTr.childNodes[colIndex].innerText == thisTr.childNodes[colIndex].innerText) {
   // 4)若前面的td未合并,后面的td都不进行合并操作
   if (i > 0 && thisTr.childNodes[colIndexArray[i - 1]].style.display != 'none') {
   lastTr = thisTr;
   continue;
   } else {
   // 5)符合条件合并td
   if (lastTr.childNodes[colIndex].hasAttribute('rowspan')) {
    var rowspan = lastTr.childNodes[colIndex].getAttribute('rowspan') - 0;
    rowspan++;
    lastTr.childNodes[colIndex].setAttribute('rowspan', rowspan);
   } else {
    lastTr.childNodes[colIndex].setAttribute('rowspan', '2');
   }
   // lastTr.childNodes[colIndex].style['text-align'] = 'center';; // 水平居中
   lastTr.childNodes[colIndex].style['vertical-align'] = 'middle';; // 纵向居中
   thisTr.childNodes[colIndex].style.display = 'none'; // 当前行隐藏
   }
  } else {
   // 5)2个tr的td内容不一样
   lastTr = thisTr;
  }
  }
 }
 }
}

3.2 在线演示

为了方便你直观查看合并效果,此处提供了一个可直接运行的示例。由于演示链接已按要求移除,请自行在本地搭建测试环境进行验证。

以上就是 ExtJS 4.2 Grid 单元格合并的完整实现方案,涵盖了原理分析、多列合并策略以及可复用的核心代码。如果你在实际开发中遇到相关问题,可以参考本文档进行调试和适配。

来源:https://www.jb51.net/article/94597.htm
上一篇Ext JS阻止键盘触发后退或刷新页面代码实现 下一篇Extjs Combobox简洁美观的写法技巧
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
Layui弹出层监听子页面键盘快捷键实现方法
前端开发 · 2026-07-06

Layui弹出层监听子页面键盘快捷键实现方法

子页面键盘事件监听需在DOM加载完成后绑定,父页无法直接监听子页按键,必须由子页自身监听后通过parent或postMessage通知父页。典型写法为子页调用父页已定义的关闭函数。需注意焦点状态、输入法及layui版本兼容性等陷阱。

Layui表单提交时携带当前页面Meta信息的实现方法
前端开发 · 2026-07-06

Layui表单提交时携带当前页面Meta信息的实现方法

Layui表单提交不会自动携带页面Meta信息,需在form on( submit )回调中手动读取meta内容并拼接到表单数据,注意后端字段映射及特殊字符编码,多meta时按需选取。

HTML5拖拽事件流状态转移监控调试
前端开发 · 2026-07-06

HTML5拖拽事件流状态转移监控调试

HTML5拖拽事件流易因漏监听或未调用preventDefault而中断。需掌握dragstart设置数据、dragover接受放置、drop触发条件等关键点。通过统一日志捕获事件上下文、识别常见状态丢失场景并配合可视化面板,可清晰定位拖拽过程断点。

uni-app实现小红书商品详情图卡片切换
前端开发 · 2026-07-06

uni-app实现小红书商品详情图卡片切换

通过手写touch事件与transform控制五张卡片,动态计算translateX、scale、opacity及z-index模拟层叠滑动效果。滑动距离超过80rpx触发切换,否则复位。图片仅渲染当前及前后两张,有效优化加载性能与渲染效率。

图像旋转倾斜与扭曲的Canvas像素矩阵变换
前端开发 · 2026-07-06

图像旋转倾斜与扭曲的Canvas像素矩阵变换

Canvas图像变形本质是操作坐标系,图像被动跟随。旋转需先平移原点至目标中心再旋转后复位;倾斜通过仿射变换矩阵实现;扭曲无原生API,可用分块模拟或转用WebGL。每次变换前保存状态,完成后恢复,避免坐标系偏移。