【问题标题】:Best practice for using DOM Level 0 in modern JavaScript applications在现代 JavaScript 应用程序中使用 DOM Level 0 的最佳实践
【发布时间】:2011-07-05 16:39:04
【问题描述】:

对于在现代 JavaScript 应用程序中使用 DOM Level 0 collections,是否有一套公认的“最佳实践”? (document.formsdocument.images等)

在使用 jQuery 的应用程序中,我注意到使用$(...).html() 更改下拉菜单的内容以切换底层节点的趋势,而不是使用element.options[]。这是因为最好避免使用 DOM 0 集合,还是因为 jQuery 使更改底层 DOM 结构变得更加容易?

编辑:我想问题的一部分包括旧功能是否可靠的跨浏览器。我记得曾几何时,IE 会自动将<tbody> 标签添加到您的表格中,而 Firefox 不会。这使得跨浏览器遍历 dom 树变得很痛苦。同样,element.options[]problems when changing the options in the collection。这些家伙是可靠的跨浏览器吗?

【问题讨论】:

  • +1 非常好的问题。我一直避免使用 em,只是因为我不相信浏览器的原生代码会以同样的方式填充和响应对这些集合的修改。
  • 有些问题可能是怪癖模式造成的,大家想想……

标签: javascript jquery dom


【解决方案1】:

首先,很好的问题。

DOM Level 0-1 功能是我最喜欢使用的工具箱。支持是巨大的。我将在下面重点介绍每个 DOM 级别 0 子集的优缺点:

DOM 0 级事件

由于这些是作为 ElementNode 的属性添加的,因此您只能拥有一个处理程序。对于某些情况(例如:事件委托),这是繁重的。但是,我觉得 DOM 足够多样化(尤其是当您的项目变得更大时)可以为 DOM 0 级处理程序提供足够的空间。如果没有库/框架来平滑旧浏览器,则 DOM 2 级侦听器很难实现。即使作为 Flash 开发人员(到处都使用侦听器),DOM 0 事件对我来说也容易得多。其中一个亮点是this 值是为您设置的(没有像其他模型那样对 IE 进行黑客攻击和咳嗽)。例如,考虑这个标记:

<div id="foo">Cick Me!</div>

现在,需要做的就是选择和附加 DOM Level 0 处理程序。

var foo = document.getElementById("foo");
function bar()
{
    console.log(this); //<div id="foo">
}
foo.onclick = bar;

这是选择元素的一种非常简单的方法,是Event.currentTarget 的替代方法;

这里有关于 DOM Level 0 事件与 DOM Level 2 事件的精彩讨论:[link]


DOM 级别 0 HTMLCollections

没有,HTMLCollections 是我最喜欢的 DOM 特性。因为为您选择了节点,所以无需运行查询。在我看来,它们是当今最被忽视的 DOM 特性。名称遍历,例如:collection["name"] 非常方便,在遍历表单时肯定会有所帮助。例如,考虑以下标记:

<form action="./" id="foo" name="foo" method="post" onsubmit="return false;">
    <fieldset>
        <legend>Dummy Form</legend>
        <input type="text" name="bar">
        <select name="baz">
            <option selected value="1">1</option>
        </select>
    </fieldset>
</form>

有很多 DOM Level 0 方法可以解决这个问题。

  1. var foo = document.forms.foo; //&lt;form id="foo" onsubmit="return false;" method="post" name="foo" action="./"&gt;

    演示:http://jsbin.com/udozoz/0edit#preview

  2. var foo = document.forms[0]; //&lt;form id="foo" onsubmit="return false;" method="post" name="foo" action="./"&gt;

    演示:http://jsbin.com/udozoz/2/edit#preview

  3. var foo = document.getElementById("foo"); //&lt;form id="foo" onsubmit="return false;" method="post" name="foo" action="./"&gt;

    演示:http://jsbin.com/udozoz/3/edit#preview

当然,方法3更优选。它是 DOM 级别 1,而不是 DOM 级别 0。但是,名称遍历自然适合 HTMLFormElement.elements HTMLCollection。由于您应该在表单元素上使用name 属性,因此无需id 属性即可轻松访问它们。

例如:var baz = foo.elements.baz;

当使用同名的单选按钮时(一次只能选择一个),您可以使用HTMLFormElement.elements HTMLCollection 来选择所有单选按钮。这是非常强大的。考虑这个标记:

<form action="./" id="foo" name="foo" method="post" onsubmit="return false;">
    <fieldset>
        <legend>Radio Buttons</legend>
        <label for="select_1">1</label>
        <input id="select_1" type="radio" name="selectable" value="a">
        <label for="select_2">2</label>
        <input id="select_2" type="radio" name="selectable" value="b">
        <label for="select_3">3</label>
        <input id="select_3" type="radio" name="selectable" value="c">
    </fieldset>
</form>

您可以使用这个简单的代码,让每个单选按钮的name 属性值为“可选”:

  1. var foo = document.forms.foo;
    var selectables = foo.elements.selectable;
    console.log(selectables); //[input#select_1 a, input#select_2 b, input#select_3 c]
    

    演示:http://jsbin.com/upiyom/edit#preview

  2. var foo = document.forms.foo;
    var selectables = foo.selectable;
    console.log(selectables); //[input#select_1 a, input#select_2 b, input#select_3 c]
    

    演示:http://jsbin.com/upiyom/2/edit#preview

选项 2 使您能够完全绕过 elements HTMLCollection。虽然肯定不如选项 1 清晰,但它今天仍在使用。

HTMLCollections 自引入 DOM Level 0 以来变得越来越多,也越来越多样化。例如,看看可用于表格的 HTMLCollections。这是惊人的。有HTMLTableElement.rowsHTMLTableElement.tBodiesHTMLTableSectionElement (thead, tbody, tfoot).rowsHTMLTableRowElement.cells。这些集合非常强大,并且使用表(允许您使用它们)使 DOM 遍历变得更加简单。


DOM 0 级属性

虽然 ElementNodes 上的属性在 DOM 级别 0 中不像现在那样多样化,但仍有一些亮点需要注意:

HTMLInputElement.defaultChecked

defaultChecked 使您能够完全绕过对HTMLInputElementchecked 属性的搜索,因为它存储了一个基于该属性值的布尔值。这意味着您不必为与 get/set/removeAttribute 相关的 IE 构建提供草率的解决方案。稍后,还将添加 defaultValue 属性来满足类似的需求。

document.lastModified [非标准]

lastModified 将存储上次更改文档的时间。这是一个很酷的小功能,但用途有限。

HTMLDocument.title

title 将为您获取文档的标题。它的用途充其量只是一个小众市场。


关于您的 tbody 问题,如果您不提倡正确的 DOM 结构,今天的浏览器会添加 HTMLTableSectionElement (tbody)。您应该了解正确的表格标记,这样将来就不会出现问题了:)。

示例标记:

错误:

<table>
    <!-- tbody will be inserted here and will wrap the tr -->
    <tr>
        <td>Hello, World!</tr>
    </tr>
</table>

对:

<table>
    <tbody>
        <tr>
            <td>Hello, World!</td>
        </tr>
    </tbody>
</table>

演示:http://jsbin.com/exomub/edit#preview


总结

需要强调的主要一点是,DOM Level 0 的大部分内容都在 DOM Levels 1 和 2 中进行了标准化。这意味着浏览器支持非常广泛(因为它确实很旧)。除了旧浏览器版本中的一些边缘情况外,使用它不应该有太多的担忧。归根结底,这是您的选择。

我想补充一点,我只是非常短暂地——在过去——受雇于使用 HTML/JavaScript 进行开发。我把它作为一种爱好,所以我不知道关于浏览器/项目出错的“恐怖故事”。

我希望这能解决一些问题。

-马特

ElementNode - 带有nodeType == 1的节点

HTMLCollection - 由浏览器收集的类似实时数组的 NodeList

【讨论】:

  • +1 这是我读过的最好的、最具教育意义的答案之一。
【解决方案2】:

这是一个非常有趣的问题。这是我的两分钱。

首先,这可能不言而喻,但这取决于您正在处理谁的代码。专业程序员的目标是遵循公司范围(或者,对于大公司,团队范围)的最佳实践,如果这些准则不鼓励或禁止 DOM 级别 0,那么您不应该使用它。如果允许或根本不提及,则决定取决于个人选择,例如您自己的代码。

现在,如果您愿意,没有明显的技术缺陷会阻止您使用 Level 0。例如,如果迭代 element.optionselement.getElementsByTagName("option")$("option", element) 慢,我会感到惊讶。第一种语法也可以说比其他语法更具可读性。

浏览器支持也不是问题。 0 级是older than dirt,十多年来,所有支持脚本的浏览器都支持它。

但是,以上是关于选择,而不是效率,这是您问题的后半部分所关注的。实际上,您可以以大致相同的效率迭代 element.options$("option", element)$(element).children("option"),但如果您必须做繁重的工作(例如,清除现有的 &lt;option&gt; 元素并添加新元素) ,那么使用element.innerHTML$(element).html() 肯定会更快。

这是因为innerHTML Level 0 属性和html() jQuery 方法(在内部使用innerHTML)都将所有标记解析和DOM 操作委托给浏览器,浏览器通常用低级语言编写并且高度针对这些任务进行了优化。相比之下,在 Javascript 循环中一个一个地删除 &lt;option&gt; 元素总是比较慢,在这种情况下,使用 DOM 级别 0 或 jQuery 的所有花里胡哨绝对没有区别。

【讨论】:

  • 我非常怀疑使用 innerHTML 是否比 OOM 级别 0 更快。请用基准测试支持它。个人基准测试发现 DOM 和 HTML 字符串操作没有区别
  • @Raynos, innerHTML DOM Level 0。还是我误解了你的评论?
  • 这取决于场景,但在 ie 和大多数浏览器中,innerhtml 通常更快。 quirksmode.org/dom/innerhtml.html
  • @Raynos,你的意思是你的基准测试表明,将innerHTML 设置为空字符串在速度方面等同于循环单个子元素和remove()ing 它们?如果是这样,我确实很惊讶(但这确实发生了:)你能提供一个链接吗?
  • @Lime 这是过时的信息,在 2006 年确实如此。请注意,浏览器测试是 IE7 和 FF2,这些信息在今天是无用的。在最近的浏览器中,差异消失了。 Benchmark。 @FredericHamidi 它们在 chrome 中是相同的。顺便说一句,任何重要的 dom 操作都与现代浏览器中的 DOM 和 innerHTML 执行相同的操作
【解决方案3】:

我首先要说的是,我不知道有任何“官方”声明,例如“DOM 级别 0 被认为有害”。这只是我个人的看法。

我认为这取决于具体情况。

我遇到的问题是,使用documents.formsdocuments.images 之类的集合来定位特定元素是有问题的,尤其是对于动态生成/修改的文档。每次看到有人写document.forms[0],我都会畏缩。

当前,甚至不那么流行的浏览器通过getElementsByTagName 之类的函数提供了模拟相同行为的能力,但以更通用和可重用的方式。当然,当您将 jQuery 添加到组合中时,根本没有理由使用这些集合。

我遇到的例外情况是,当您在非 jQuery 环境中工作并且您正在访问诸如 selectElement.optionstbodyElement.rows 之类的内容时。在这些情况下,当您执行添加或删除项目等基本操作时,它确实会更简单。

【讨论】:

    【解决方案4】:

    在使用 jQuery 的应用程序中,我注意到一种趋势,例如,使用 $(...).html() 来更改下拉菜单的内容以切换出底层节点,而不是使用 element。选项[]。这是因为最好避免使用 dom0 集合,还是因为 jQuery 可以更轻松地更改底层 DOM 结构?

    这很容易解释。 95% 的 jQuery 开发人员不知道 DOM API 存在或可以使用这一事实。

    jQuery 被滥用于 DOM 更容易做的事情只是因为人们没有学习 DOM。

    我个人会说使​​用 DOM 0,但话又说回来,我个人会说使​​用 DOM shim 而不要使用 jQuery。这都是选择的问题。

    做最可维护的

    【讨论】:

    • 我真的不能怪别人不想学习 DOM……你能吗?
    • @nickf DOM 远没有你想象的那么糟糕。现在已经不是 90 年代了。 DOM3 API 非常可靠。不要将 DOM 与 IE6-8 暴露的可悲 API 混淆。
    • 这不就是jQuery吗?
    • 绝对是任意的,但为什么这么糟糕?
    【解决方案5】:

    无论好坏,我主要使用纯 DOM 0 级功能进行调试。检查 document.forms[0].elements 的输入有时会更快。

    当我检查一些使用一些深奥(或我根本不知道)框架的页面时,我也会使用这些功能。我没有时间深入研究这些抽象而只是直接检查集合。

    我相信您最好了解所有这些 DOM 零武器库,但如果您了解所有这些现代纯 DOM don't-know-which-level-it-is-exaclty 级别的 API,那就更好了。例如,ClassList 集合很好。我的意思是,如果您使用的是框架,您应该始终知道,为什么您正是需要它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-06-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-20
      • 1970-01-01
      • 2012-03-08
      相关资源
      最近更新 更多