首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >转换/移动大量DOM元素的最有效方法

转换/移动大量DOM元素的最有效方法
EN

Stack Overflow用户
提问于 2016-03-21 10:54:31
回答 3查看 1.9K关注 0票数 24

不久前,我为AngularJS建立了一个树形表结构(带有角质材料)。

我的目标是使它只在大屏幕上工作(1280及更高版本),但现在我想更新它,使它在不限制数据的情况下在较小的设备(主要是平板电脑)上工作。由于性能原因,我希望保持HTML尽可能简单(树表可以有1000+行,因此为单行创建更复杂的HTML将延长附加和呈现表行所需的时间(行是动态的,因此不仅涉及初始呈现))。

我想出了一个想法,我将保留包含名称的第一个单元格的“固定”部分,并滚动包含所有度量的第二部分,并同步滚动。

当前单行HTML:

代码语言:javascript
复制
<div class="tb-header layout-row">
  <div class="tb-title"><span>Name</span></div>
  <div class="tb-metrics">
     <div class="layout-row">
        <div class="tb-cell flex-10">812</div>
        <div class="tb-cell flex-7">621</div>
        <div class="tb-cell flex-4">76.5</div>
        <div class="tb-cell flex-7">289</div>
        <div class="tb-cell flex-4">46.5</div>
        <div class="tb-cell flex-7">308</div>
        <div class="tb-cell flex-4">49.6</div>
        <div class="tb-cell flex-7">390</div>
        <div class="tb-cell flex-4">48.0</div>
        <div class="tb-cell flex-7">190</div>
        <div class="tb-cell flex-4">23.4</div>
        <div class="tb-cell flex-7">0</div>
        <div class="tb-cell flex-4">0.0</div>
        <div class="tb-cell flex-8">6.4</div>
        <div class="tb-cell flex-8">0.0</div>
        <div class="tb-cell flex-8"></div>
     </div>
  </div>

我的想法是在父容器上使用touchmove事件(包装整棵树并将其作为指令绑定),并检查touchmove何时在度量部分开始,然后计算我应该移动度量的值。那部分运作得很好。当我想在.tb-metrics >上应用偏移量时,问题就开始了。

我第一次尝试使用jQuery:

代码语言:javascript
复制
function moveMetrics( offset ) {
  var ofx = offset < 0 ? (offset < -getMetricsScrollWidth() ? -getMetricsScrollWidth() : offset) : 0;
  $('.tb-metrics').children().css('transform', 'translateX(' + ofx + 'px)');

  /*...*/

}

不幸的是,当表包含更多行时,此解决方案非常慢(我不能缓存行,因为它们是动态的)。

在我的第二次尝试中,一次尝试尽量避免DOM操作。为了实现这一点,我决定将<script>标记添加到包含应用于.metrics > .layout-row的css的dom中。

解决方案:

代码语言:javascript
复制
function moveMetrics( offset ) {
  var ofx = offset < 0 ? (offset < -getMetricsScrollWidth() ? -getMetricsScrollWidth() : offset) : 0
    , style = $( '#tbMetricsVirtualScroll' )
    ;

  if ( !style.length ) {
    body.append( '<style id="tbMetricsVirtualScroll">.tb-metrics > * {transform: translateX(' + ofx + 'px)}</style>' );
    style = $( '#tbMetricsVirtualScroll' );
  } else {
    style.text( '.tb-metrics > * {transform: translateX(' + ofx + 'px)}' );
  }
  /*...*/
}

但是,当表包含大量行时,它似乎不会更快。因此,这不是DOM操作,而是呈现/绘制视图似乎是这里的瓶颈。

我尝试创建某种虚拟滚动,但是由于树结构对于不同的数据集不同,并且可以有“无限”的级别(每行可以在新的ng-repeat中包含子行),这是一个非常困难的任务。

我将欣赏任何关于如何在这种情况下提高性能而不使用虚拟卷轴的想法。

编辑:

Chrome时间线的屏幕截图显示,滚动的大部分时间都是通过呈现来完成的(我猜是因为复杂的DOM结构)。

编辑2:

我不会说我实现了绝对平滑的滚动,但我发现了一些显著的性能改进(其中一些并不明显,结果比我预期的要好)。

  • 简化类选择器:.tb-header > .tb-metrics > .tb-cell.tb-specific-cell慢得多(似乎解析更复杂的选择器需要更多时间?)
  • 从转换元素中删除不透明度和框影
  • 尝试将转换后的元素分发到新层(使用css、will-change和/或translateZ(0))
EN

回答 3

Stack Overflow用户

发布于 2021-05-04 18:59:22

我过去也曾遇到过类似的问题,那就是大桌子。因此,把UX方面的东西排除在等式之外--为了达到你的目标--可能有以下几点。也许你可以试着换个方法,而不是把第一张桌子固定下来,然后把剩下的都搬出去。也就是说,将表保存在一个超越式容器中,以允许水平滚动,并在垂直滚动时使用translateY (第一列表格单元格)移动。

按照我所描述的方式,您可以检索第一个表格单元格的最大宽度(或者更好地,设置一个固定的宽度),绝对将每个单元格放置在左边--小心,相对容器应该位于覆盖表的外部,在每一行的第二个表单元格上设置一个填充--左侧,等于第一个表格单元格,然后当垂直滚动时,添加一个负的translateY值。然后,选择器应该易于访问。布局-行..table cell:first-child‘。

另外,对于次要的优化,不要使用.css(),而是尝试使用.attr('style', value)。另外,我看到您有动态内容,不能缓存它,但是,也许您应该考虑每次操作DOM时缓存一次。缓存的选定元素仍然比在每个滚动/touchmove事件中计算选择器要好。

最后但并非最不重要的一点是,你也可以添加某种脱扣技术,这样滚动就不会出现在每个滚动框上,而是可能每10帧就有一次--它仍然是肉眼看不见的,或者无论如何都比瓶子更好--用所有这些重绘事件来吸引浏览器。

票数 1
EN

Stack Overflow用户

发布于 2021-06-23 15:41:19

我建议你用懒装。请注意,在延迟加载中有4个选项:有四种常见的实现延迟加载设计模式的方法:延迟初始化;虚拟代理;鬼和值保持器。它们各有优缺点(链接)。

祝好运

票数 1
EN

Stack Overflow用户

发布于 2019-04-16 15:43:44

所以我自己并不是专家,但我确实有一些我认为值得提及的想法。如果我理解正确的话,问题是这个项目需要越来越多的HTML元素,这会不断降低性能

这里有3个js概念,我认为这些概念可以帮助您。

*动态创建html元素:

代码语言:javascript
复制
let div = document.createElement('div');

*动态地创建要用于元素的属性,并能够动态地分配这些属性

代码语言:javascript
复制
function attributeSetter(atr){
   div.setAttribute('class', atr)
}

这样,您就可以在需要的地方调用函数,并动态地给它命名任何类名。此外,您可以任意多次使用div元素。

最后的概念。您还可以动态地添加预先生成的类,假设它们已经在css中预先定义了,如下所示。

代码语言:javascript
复制
div.classList.add("my-class-name")

另一个可能会派上用场的概念是解构。

代码语言:javascript
复制
let myClasses = ['flex-10', 'flex-7', 'flex-4', 'flex-9'] //etc
let [flex10, flex7, flex4, flex9] = myClasses;

这将将所有这些字符串转换为变量,您可以轻松地遍历这些变量并将它们动态添加到attributeSetter函数中,如下所示:

代码语言:javascript
复制
attributeSetter(flex10)

不管你需要什么。我不知道这是否有帮助,或者这是否回答了你的问题,但至少有一些想法。祝你好运:)

票数 -4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36129133

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档