【问题标题】:HTML DOMs, ids vs no ids?HTML DOM,ID 与无 ID?
【发布时间】:2012-08-05 00:22:22
【问题描述】:

假设您有一个容器 div,其中包含数百个非常简单的子元素,例如:

<div id="stuffContainer" class="item-container">
    <div class="single-item"></div>
    <div class="single-item"></div>
    <div class="single-item"></div>
    <div class="single-item"></div>
    <div class="single-item"></div>
    <div class="single-item"></div>
    [...]
</div>

是否会以任何(有意义的)方式为每个单个元素损害(客户端)性能包括一个唯一的 ID,无论是渲染、javascript 还是内存方面?

我问的原因:我可能需要不时引用特定项目,我试图弄清楚我是否应该提前预先计算我需要选择哪些项目,给它们 id 并离开休息一下,或者只是为所有这些人分配 id,这将使过程更直接,但我想知道这是否可能是一种矫枉过正。

如果有人可以谈论现代浏览器如何处理这一点以及为什么它会/不会在浏览器如何呈现和管理 DOM 方面产生影响,那么奖励积分(如果我真的可以给他们的话)。

【问题讨论】:

  • 其实你可以给积分,你看好像有很多。只需设置赏金;-)
  • 我创建了a simple JSPerf test,得到的结果很复杂(尽管测试可能不太好),即使在不同窗口的同一个浏览器(Chrome)中也是如此。解析 HTML 可能会花费大部分时间,因此 here is a version 是每个元素具有相同数量的属性,但相同的混合结果。我认为性能方面(比如建立索引等)这真的没有什么区别。
  • @FelixKling 很有趣,感谢您整理测试;在我这边(Chrome 20 和 Firefox 14)看起来没有 id 版本稍微(非常稍微)快。

标签: javascript html css


【解决方案1】:

最多,我猜浏览器会做的几件事就是在构建 DOM 结构时将 ID 作为属性分配给每个元素,并将 ID 存储在哈希表中以方便操作像#id 选择器、document.getElementById() 和稍后的 idref 查找。我认为这不会对观察到的性能或内存使用造成哪怕是轻微的影响,因为哈希表和 DOM 设计得非常好。

也就是说,如果您的元素根本不需要 ID,那么为它们分配 ID 毫无意义;相反,您最终会得到大量的标记膨胀。正如 Dan Davies Brackett 所提到的,这显然会导致速度变慢,因为它取决于客户端连接。

如果您有能力使用 CSS 选择器引用这些元素,则始终可以使用 :nth-child() 来查找特定的子元素。如果您需要支持旧版浏览器,总有一个 CSS2 解决方案。请参阅以下问题:

【讨论】:

  • 我喜欢“观察到的性能”这个词,我完全是在偷那个。虽然没有选票:(
  • 很好的答案,谢谢。我希望我可以让一些 Chrome 或 Mozilla 开发人员加强并发布一个冗长的技术答案,说明这不会影响现代浏览器的性能,但我想这不会发生 :-)
  • @Mahn:嗯,每个人至少有一个偶尔会过来。也许如果你幸运的话......
  • 根据页面的排列方式/每个项目有多少 html 内容和其他因素,在回流前端为数百个项目循环和分配 ID 可能会变得非常难看。
  • @Erik Reppen:听起来很讨厌。我现在已经从我的答案中删除了那一点。
【解决方案2】:

正如 BoltClock 所暗示的,大多数性能问题的答案是“测量它!”。也就是说,您错过了一个潜在的衡量轴:下载大小。将 ID 属性添加到比必要更多的元素会增加字节权重,尤其是因为元素必须是唯一的(这意味着它们不会像非唯一属性那样压缩)。

【讨论】:

    【解决方案3】:

    最初的 Q 对内容/目的不是很具体,应该会有变化,这取决于 IMO。在某一点上,我绝对不同意当前接受的答案。循环并附加新的 ID 到大量已经渲染的 HTML 可能会导致大量的回流计算,根据 'stuffContainer' 的含义,这可能会变得很丑。

    如果必须的话,首先从服务器使用 ID 构建或在客户端作为单个块注入

    作为一般规则,如果可以避免重复更改 DOM,请避免这样做。就加载页面的浏览器 IMO 而言,预先在服务器上构建的 ID 可以忽略不计。当然,它是一个更大,因此更慢的哈希表,但如果我们谈论的是数百而不是数万,我严重怀疑你会注意到。

    对于从数据构建的选择列表之类的情况,一种更有效的客户端方法是首先构建为字符串,带有 ID 和所有内容,然后分配给容器的 innerHTML,或者如果像我的一些js 聊天的同事们,你在每个可能的用例中都有一个关于 innerHTML 的奇怪挂断,即使它现在在规范中,至少首先构建并附加到文档片段。然后将其附加到您的容器中。

    ID 上的数据属性或类

    IMO,气味因素与性能无关,而是 HTML 膨胀和创建不需要的 ID 依赖项,从而阻止其他可能在您的单项 div 上找到自定义 ID 有用的东西。 ID 绝对是缩小 JavaScript 中元素查找范围的理想方式,但在容器包含内容的情况下,您将从 ID 容器中获得比 ID 为每个子项 ID 更大的灵活性。单步与两步的性能增益不值得将通用 ID 应用于元素的灵活性成本。

    作为替代方案,您可以使用具有唯一值或数据属性的类,例如'data-itemId="0"'。对于较小的 HTML 集,例如自定义选择列表,其中 ID 连接到某些服务器端索引系统以便于更新数据,我倾向于使用这种高度可见的方法,因为它更容易理解架构,但它增加了很多在可能涉及成百上千个项目的情况下跟踪不必要的属性。

    或者理想情况下(在大多数情况下),事件委托

    最理想的情况是,IMO,如果您只关心您单击的子单项元素,而不是它的“ID”是这些单项的顺序,则您完全避免使用其他属性-item 容器将保持静态,您可以放心地假设这些位置是有效的 ID。或者,这可能适用于非静态单项集的另一种情况是,如果您在一组对象中获得单项数据,这些对象会被打乱,然后用于在用户启动的排序上构建和替换 HTML,这些排序总是将 HTML 的顺序与其他数据跟踪依赖项联系起来。

    非 Jquery 方法(未经测试):

    var myContainer = document.getElementById('stuffContainer');
    //ignore following indent goof
    
        myContainer.addEventListener('click', function(e){
            var
                singleItem,
                elementOriginallyClicked = singleItem = e.target,
                stuffContainer = this;
    
            //anything clicked inside myContainer will trigger a click event on myContainer
            //the original element clicked is e.target
            //google 'event bubbling javascript' or 'event delegation javascript' for more info
    
            //climb parentNodes until we get to a single-item node
            //unless it's already single-item that was originally clicked
            while( !singleItem.className.match(/[=\s'"]single-item[\s'"]/g) ){
                singleItem = singleItem.parentNode;
            }
    
            //You now have a reference to the element you care about
    
            //If you want the index:
    
            //Get the singleItem's parent's childNodes collection and convert to array
            //then use indexOf (MDN has normalizer for IE<=8) to get the index of the single-item
    
            var childArray = Array.prototype.slice.apply(stuffContainer.childNodes,[0]),
            thisIndex = childArray.indexOf(singleItem);
    
            doSomethingWithIndex(thisIndex);
            //or 
            //doSomethingWithDOMObject(singleItem);
    
        } );
    

    或简单的 JQuery 委托样式(也未经测试):

    $('#someContainer').on('click','.single-item', function(){
        var $_singleItem = $(this), //jq handles the bubble to the interesting node for you
        thisIndex = $_singleItem.index(); //also getting index relative to parent
    
        doSomethingWithIndex(thisIndex);
        //or
        //doSomethingWithJQObject($_thisItem);
    } );
    

    【讨论】:

    • 我几乎同意你写的一切,你有我的赞成票,但我必须说实话,我一直在寻找关于浏览器如何反应的答案,如果 ID 是生成的服务器当我发布问题时,@BoltClock 更直接地涵盖了这个问题。但我会接受你的回答,我可以接受两次。
    • 没有问题。赞成票表示赞赏。我已经学会接受成为 TLDR 女仆而不是新娘。
    【解决方案4】:

    使用id 属性不会显着降低性能,即使使用了数百次。虽然手动挑选需要唯一标识的元素会稍微快一些,但在我看来,性能改进并不能证明这样做所需的额外时间是合理的。

    话虽如此,我不确定在这种情况下是否需要 id 属性。如果您要使用 jQuery,您可以选择第四个元素,例如,使用以下代码:

    $('.single-item').eq(3);
    

    【讨论】:

    • 可能通过 id 选择它们仍然比使用 jQuery 的 eq 更快,不是吗?
    • @Mahn - 是的,但同样,我不认为性能改进证明花费时间添加每个 id 属性是合理的。此外,删除 id 属性会减小文件大小。
    【解决方案5】:

    在这种情况下,对所有内容都使用 id 有点矫枉过正。我知道您来自哪里,但是如果您使用像 jQuery 这样的语言,考虑到结构,选择子项会很容易。下面是一个示例,说明您如何知道点击了哪个 div:

    $('.single-item').click(function()
    {
        var yourClickedDiv = $(this);
        // execute the rest of your code here
    });
    

    这只是一个小例子,但正如您所见,您可以访问每个 div 以执行您需要做的任何事情。这将减少 html 文件因过多的 id 标签而变得杂乱无章,同时保持文件大小更小(更不用说避免生成每个唯一 id 的麻烦了)。

    【讨论】:

    • jQuery 代码在性能方面存在很大问题。如果该类有数百个元素,则应该使用event delegation
    • 为了节省带宽而减少 id 属性有点矛盾,然后包含一个 4,000 行的库作为替代方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-08
    • 2014-07-16
    • 1970-01-01
    • 2022-12-05
    相关资源
    最近更新 更多