使用 Javascript 设置高度很慢

Set height with Javascript is slow

我有两个 html table,其中包含所有类型的元素(跨度、输入、select、...),我想强制那些 tables 具有相同的高度。不幸的是,在 trtd 上设置属性 'height' 不起作用(如果包含至少一个超过给定高度的元素,则该行更高)。

我还没有找到如何用 css 强制行高的方法,所以我写了一个 Javascript 循环所有行的函数,检查行的高度左边 table 与右边相应行的高度 table 如果它们不同,我将高度(更改样式)设置为最大值。

它可以工作...但是如果 table 有很多行,它会非常非常慢!我认为这是因为每次更改样式都会导致重排。

有什么提示吗?请注意,我无法合并两个 tables.

这里我的代码被剪掉了,但也许我需要一个完全不同的方法...

var rightTableRows = mainTable.children("tbody").children("tr:parent");
var leftTableRows = colHeader.children("tbody").children("tr:parent");

for (chr=0;chr < leftTableRows .length;chr++) {
    var rowLeft = leftTableRows [chr];
    var heightleft = rowLeft.offsetHeight;
    var rowRight = rightTableRows[chr];
    var heightright = rowRight.offsetHeight;
    if(heightleft != heightright){
            console.log("left: "+heightleft +" - right: "+heightright);
        if(heightleft>heightright){
           rowRight.setAttribute("style","height:"+heightleft+"px");
        }else{
           rowLeft.setAttribute("style","height:"+heightright+"px");
        }
    }

}

不确定原因,但是:

将行高样式设置为 td 会增加 td 的高度,从而增加 tr 的高度。 如果没有,你可以将 td 的内容封装在 div 中,然后在上面设置行高。

另一种加快速度的方法是为所有元素设置相同的样式 class。 然后在计算高度后设置 class 的行高。这将更改使用此 class 的所有元素的高度。 阅读 Javascript 的 styleSheet 对象以了解如何做到这一点....

如果您不知道单元格的最大高度。您可能应该将 table 转换为 div 并使用 display flex

这将允许您拥有您想要的行为,但将意味着您更改 HTML 代码的结构。

您将不得不交替使用第一个 table 和第二个的元素。

我将从 jfriend00 的 answer 中摘录一段。一定要为他们的回答点赞。

DOM 修改

  1. 回流已排队。 ....
  2. 请求某些属性会触发立即回流。 上述规则存在一些例外情况,您希望确保避免。例如,如果您请求某些 DOM 属性需要正确布局才能准确报告 属性 值,并且存在来自先前修改的未决布局,则浏览器可能会在返回之前同步重新布局文档属性 你请求了。这些类型的属性通常涉及屏幕位置和其他明显受文档布局影响的属性。如果您想查找更多详细信息,有很多关于此主题的文章。在许多情况下,您的代码无论如何都不会使用这些属性,但如果是这样,通常的解决方法是在对 DOM.

    [=38= 进行任何更改之前先请求所有需要的属性]
  3. 一次批量处理所有 DOM 更改。 最糟糕的做法是进行 DOM 更改,等等用计时器几毫秒,进行另一个 DOM 更改,用计时器等待几毫秒等等,因为你将有 DOM 更改、回流、重绘、DOM 更改、回流、重绘等... 相反,请确保您在 Javascript 的一个同步片段中一次完成所有未决 DOM 更改。这将允许浏览器对回流和重绘进行排队,并且在您完成所有 DOM 更改后仅执行一次。如果您想更智能地进行批处理,您将折叠对同一元素的修改,以便它只用最终值处理一次。因此,如果 elementA 首先被赋予一个新值 3,然后在同一批次中被赋予一个值 4,那么在处理这批数据时,您希望跳过 3 而只处理 4。

  4. DOM修改有时可以优化 ....


第 2 点和第 3 点非常相关,您将获取每一行的 offsetHeight,然后设置一个新值。 所以更改不是批量的。每次更改都会导致重排,因为您将在下一行的下一次迭代中获得 offsetheight。

要优化,请读取所有行并将所有未决更改保存在内存中。 然后在新循环中应用新的行高。从内存中提取新值的位置。

您正在反复读取然后写入 DOM。这被认为是性能的一大禁忌。正确的方法是先完成所有 'reads',然后再完成所有 'writes' - 否则你会在两者之间强制执行 reflows/calculations。实际上,使用两个循环会更好,一个循环找到正确的高度,然后第二个循环应用它们:

var rightTableRows = mainTable.children("tbody").children("tr:parent");
var leftTableRows = colHeader.children("tbody").children("tr:parent");
var length = leftTableRows.length;
var heights = [];

for (var chr = 0; chr < length; chr++) {
    var rowLeft = leftTableRows[chr];
    var heightleft = rowLeft.offsetHeight;
    var rowRight = rightTableRows[chr];
    var heightright = rowRight.offsetHeight;

    if (heightleft > heightright) {
        heights.push({
            elem: rowRight,
            height: heightleft
        });
    } else {
        heights.push({
            elem: rowLeft,
            height: heightright
        });
    }
}

for (var i = 0; i < heights.length; i++) {
    heights[i].elem.style.height = heights[i].height + 'px';
}

现在元素及其新高度作为对象存储在数组中,然后您只需遍历数组即可。在第一个循环之后,您再也不会从 DOM 中读取。其他优化包括删除不需要的 if() 语句,缓存 HTML 节点列表的长度,以及专门编写 style.height 而不是 setAttribute()(更慢)。