diff --git a/docs/table/detail/options.md b/docs/table/detail/options.md index f9053930..805693c0 100644 --- a/docs/table/detail/options.md +++ b/docs/table/detail/options.md @@ -254,6 +254,17 @@ height: function(){ string +- + + +syncFixedRowHeight 2.12+ 实验性 + + +是否强制计算表格主区域的行高度并同步到固定列区域。开启后会对表格性能有一定的影响,仅适用于行高度自适应的场景。 +该功能使用 [ResizeObserver](https://developer.mozilla.org/zh-CN/docs/Web/API/ResizeObserver) 实现,如果你的浏览器不支持 `ResizeObserver`,可以尝试添加 [polyfill](https://github.com/que-etc/resize-observer-polyfill) 来解决兼容性问题。 + + +boolean - diff --git a/src/modules/table.js b/src/modules/table.js index d851eae3..28d7b450 100644 --- a/src/modules/table.js +++ b/src/modules/table.js @@ -151,9 +151,12 @@ layui.define(['lay', 'i18n', 'laytpl', 'laypage', 'form', 'util'], function(expo var ELEM_TOOL_PANEL = 'layui-table-tool-panel'; var ELEM_EXPAND = 'layui-table-expanded'; var DISABLED_TRANSITION = 'layui-table-disabled-transition'; + var FIXED_HEIGHT_PATCH = 'layui-table-fixed-height-patch'; var DATA_MOVE_NAME = 'LAY_TABLE_MOVE_DICT'; + var resizeObserver = lay._createResizeObserver(MOD_NAME); + // thead 区域模板 var TPL_HEADER = function(options){ var rowCols = '{{#var colspan = layui.type(item2.colspan2) === \'number\' ? item2.colspan2 : item2.colspan; if(colspan){}} colspan="{{=colspan}}"{{#} if(item2.rowspan){}} rowspan="{{=item2.rowspan}}"{{#}}}'; @@ -288,6 +291,7 @@ layui.define(['lay', 'i18n', 'laytpl', 'laypage', 'form', 'util'], function(expo var that = this; that.index = ++table.index; that.config = $.extend({}, that.config, table.config, options); + that.unobserveResize = $.noop; that.render(); }; @@ -318,6 +322,11 @@ layui.define(['lay', 'i18n', 'laytpl', 'laypage', 'form', 'util'], function(expo options.elem.attr('id') || that.index ); + // 重复 render 时清理旧实例 + if(thisTable.that[id] && thisTable.that[id] !== that){ + thisTable.that[id].dispose(); + } + thisTable.that[id] = that; // 记录当前实例对象 thisTable.config[id] = options; // 记录当前实例配置项 @@ -460,6 +469,7 @@ layui.define(['lay', 'i18n', 'laytpl', 'laypage', 'form', 'util'], function(expo that.pullData(that.page); // 请求数据 that.events(); // 事件 + that.observeResize(); // 观察尺寸变化 }; // 根据列类型,定制化参数 @@ -622,6 +632,7 @@ layui.define(['lay', 'i18n', 'laytpl', 'laypage', 'form', 'util'], function(expo // 自定义 css 属性 if (options.css) text.push(options.css); + text.push('.' + FIXED_HEIGHT_PATCH + '{height:auto;}'); // 生成 style lay.style({ @@ -1521,6 +1532,11 @@ layui.define(['lay', 'i18n', 'laytpl', 'laypage', 'form', 'util'], function(expo }, 50); that.haveInit = true; + // reloadData 或 renderData 时,tbody 高度可能不变,需要主动同步 + if(that.needSyncFixedRowHeight){ + that.calcFixedRowHeight(); + } + layer.close(that.tipsIndex); }; @@ -2530,12 +2546,20 @@ layui.define(['lay', 'i18n', 'laytpl', 'laypage', 'form', 'util'], function(expo var othis = $(this); var index = othis.index(); if(othis.data('off')) return; // 不触发事件 - that.layBody.find('tr:eq('+ index +')').addClass(ELEM_HOVER) + var trsElem = that.layBody.find('tr:eq('+ index +')'); + trsElem.addClass(ELEM_HOVER); + if(that.needSyncFixedRowHeight){ + that.fixedRowHeightPatchOnHover(this, trsElem, true); + } }).on('mouseleave', 'tr', function(){ // 鼠标移出行 var othis = $(this); var index = othis.index(); if(othis.data('off')) return; // 不触发事件 - that.layBody.find('tr:eq('+ index +')').removeClass(ELEM_HOVER) + var trsElem = that.layBody.find('tr:eq('+ index +')'); + trsElem.removeClass(ELEM_HOVER); + if(that.needSyncFixedRowHeight){ + that.fixedRowHeightPatchOnHover(this, trsElem, false); + } }).on('click', 'tr', function(e){ // 单击行 setRowEvent.call(this, 'row', e); }).on('dblclick', 'tr', function(e){ // 双击行 @@ -2922,6 +2946,119 @@ layui.define(['lay', 'i18n', 'laytpl', 'laypage', 'form', 'util'], function(expo } }; + Class.prototype.dispose = function(){ + var that = this; + + that.unobserveResize(); + for (const propName in that) { + if(lay.hasOwn(that, propName) && propName !== 'config'){ + that[propName] = null; + } + } + + } + + Class.prototype.calcFixedRowHeight = function(){ + var that = this; + + var tableElem = that.layMain.children('table'); + var leftTrs = that.layFixLeft.find('>.layui-table-body>table>tbody>tr'); + var rightTrs = that.layFixRight.find('>.layui-table-body>table>tbody>tr'); + var mainTrs = tableElem.find('>tbody>tr'); + + // 批量获取主表格行高,设置高度以优化性能 + var heights = []; + mainTrs.each(function() { + heights.push(that.getElementSize(this).height); + }); + + if (leftTrs.length) { + leftTrs.each(function(i) { + if (heights[i]) { + this.style.height = heights[i] + 'px'; + } + }); + } + + if (rightTrs.length) { + rightTrs.each(function(i) { + if (heights[i]) { + this.style.height = heights[i] + 'px'; + } + }); + } + } + + // 鼠标悬停于某一列纵向移动时,若单元格会出现横向滚动条,此时 tbody 高度不变,需要修复行高 + Class.prototype.fixedRowHeightPatchOnHover = function (targetEl, trsElem, isEnter) { + var that = this; + var style = that.elem.children('style')[0]; + var selector = '.' + FIXED_HEIGHT_PATCH; + + trsElem.toggleClass(FIXED_HEIGHT_PATCH, isEnter); + // 将当前鼠标悬停行的高度同步到 FIXED_HEIGHT_PATCH (所有区域)类名的样式中 + if (isEnter) { + lay.getStyleRules(style, function (item) { + if (item.selectorText === selector) { + item.style.setProperty('height', that.getElementSize(targetEl).height + 'px', 'important'); + } + }) + } else { + // 将当前鼠标悬停行主区域的行高同步到固定列行 + var height; + lay.getStyleRules(style, function (item) { + if (item.selectorText === selector) { + item.style.setProperty('height', 'auto'); + } + }) + trsElem = trsElem.filter(function () { + var tr = $(this); + var isFixed = tr.closest(ELEM_FIXED, that.layBox).length > 0; + if (!isFixed) { + height = that.getElementSize(tr[0]).height; + } + return isFixed; + }) + + trsElem.css('height', height); + } + } + + Class.prototype.observeResize = function(){ + var that = this; + + if(!resizeObserver) return; + + that.unobserveResize(); + + var el = that.elem[0]; + var tableEl = that.layMain.children('table')[0]; + // 显示或隐藏时重置列宽 + resizeObserver.observe(el, $.proxy(that.resize, that)); + + // 同步固定列表格和主表格高度 + var lineStyle = that.config.lineStyle; + var isAutoHeight = lineStyle && /\bheight\s*:\s*auto\b/g.test(lineStyle); + // 只重载数据时需要主动同步高度,因为 tbody 大小可能不变 + var needSyncFixedRowHeight = that.needSyncFixedRowHeight = (that.layBody.length > 1) && (that.config.syncFixedRowHeight || (that.config.syncFixedRowHeight !== false && isAutoHeight)); + + if(needSyncFixedRowHeight){ + resizeObserver.observe( + tableEl, + $.proxy(that.calcFixedRowHeight, that) + ); + } + + that.unobserveResize = function(){ + resizeObserver.unobserve(el); + if(needSyncFixedRowHeight){ + resizeObserver.unobserve(tableEl); + } + + that.unobserveResize = $.noop; + } + }; + // 全局事件 (function(){ // 自适应尺寸