【问题标题】:What repercussions do I get by using an undefined HTML element?使用未定义的 HTML 元素会产生什么影响?
【发布时间】:2014-07-23 23:16:06
【问题描述】:

为了编写更具表现力的 HTML,我觉得自定义 HTML 元素对于我可能编写的任何 web 应用程序或文档来说都是一种很好的方式,以便在不使用 cmets 的情况下从标签名称本身散发出良好的含义。

看来我可以定义一个自定义 HTML 元素:

document.registerElement("x-el");

不过,我似乎也可以在定义之前使用自定义元素:

<body>
    <x-salamander>abc</x-salamander>
</body>

甚至:

<salamander>abc</salamander>

我认为这是无效的 HTML,但是 Firefox 和 Chromium 都继续显示该元素而没有任何问题或控制台警告。

我什至可以在没有浏览器抱怨的情况下执行以下操作:

document.getElementsByTagName("salamander")[0]

在 CSS 中使用这个标签名称作为选择器也可以正常工作。那么,如果我以这种方式使用未声明的元素,可能会遇到什么问题呢?

【问题讨论】:

  • 为什么不使用data-* 属性来存储自定义数据? data-x-chicken="woof i am a chicken"Roflmao 顺便说一句。
  • 你会用那些无效的标记名破坏互联网,现在一切都在地狱。
  • @SergeKuharev - 所以如果可以摆脱编写糟糕的代码,我们都应该这样做吗?
  • 最大的问题是,“为什么?”

标签: javascript html css dom


【解决方案1】:

您尝试做的事情的问题不是我们可以告诉您它会以某些预期的方式中断。就是当您以这种方式偏离标准时,没有人知道会发生什么。根据定义,它是未定义的,并且看到它的浏览器的行为也是未定义的。

也就是说,它可能会很好用!以下是要记住的事项:

  1. HTMLUnknownElement 接口是您调用它以使其以受支持的方式工作 - 据我在 5 分钟的搜索中得知,它是在 HTML5 规范中引入的,因此在适当使用它的 HTML5 浏览器中,这不再是一个未定义的场景。这就是registerElement 发挥作用的地方,它可以采用 HTMLUnknownElement 并使其为人所知。
  2. 浏览器通常非常擅长处理意外标记...但它并不总是能带来好结果(请参阅:quirks mode)。
  3. 并非所有浏览器都是一样的。 Chrome、Firefox、Safari、Opera,甚至 IE 都可能有一些可靠的方法来可靠地处理这些元素(甚至是 HTML5 之前的版本)......但我不知道屏幕阅读器 (Lynx) 或其他各种深奥的、过时的、利基的甚至未来的浏览器都会使用它。
  4. 每个人都说过同样的话,但值得注意的是:您将无法通过验证。只要您知道它们是什么以及为什么会出现验证错误,您的网页上就可以出现验证错误,这也符合条件,但您最好有充分的理由。

浏览器有很长的历史,他们会接受你给他们的任何东西,并尝试做一些合理的事情,所以你可能会没事,如果你对主要针对 HTML5 浏览器感兴趣,那么你很可能会没事。与所有与 HTML 相关的内容一样,唯一通用的建议是测试您的目标人群。

【讨论】:

    【解决方案2】:

    我可以看到的第一个问题是 IE8 及更低版本无法始终如一地应用您的样式。即使使用“css 重置”,我也会在 IE8 中遇到问题。浏览器知道它是否在处理块、内联块、列表等很重要,因为许多 CSS 行为是由元素类型定义的。

    其次,我从未尝试过,但如果您使用 jQuery 或其他框架,我不认为它们是为将非 HTML 标记作为目标而构建的。您可以为您的编码人员创建问题。

    而且 HTML 验证器可能会导致心脏病发作,因此您会失去一个有价值的工具。

    【讨论】:

    • 谁在乎 html 验证器?我的意思是,尝试验证 google.com 输出。它“无效”——但它确实按预期工作。
    • jQuery 选择自定义 html 元素没有问题:jsfiddle.net/SXqtR。自定义标签仍然是一个 html 标签,它不是“新”的东西。
    • jQuery 选择器与自定义标签和属性完美配合。
    • @PeterAronZentai 抱歉,我不得不说验证器很重要。它们有助于执行标准。 Google 不通过验证器进行验证也没关系。您应始终尽最大努力编写标准中。
    【解决方案3】:

    你在这里重新发明了轮子。 AngularJS 已经通过它所谓的 directives 解决了添加 HTML 元素和属性的问题:

    Angular 的 HTML 编译器允许开发者教浏览器新的 HTML 语法。编译器允许您将行为附加到任何 HTML 元素或属性,甚至创建新的 HTML 元素或属性 具有自定义行为。 Angular 将这些行为扩展称为 指令。

    Angular 的目标更广泛,因为它将 HTML 视为一种用于构建应用程序的工具,而不仅仅是显示文档。对我来说,这个更广泛的目标为您问题中描述的扩展 HTML 的能力赋予了真正的意义和目的。

    【讨论】:

    • “不要戴那顶帽子,不要重新发明轮子,那边的泳衣制造商已经发明了衣服”。
    【解决方案4】:

    您应该使用命名空间版本document.createElementNS 而不是普通的document.createElement。在下面的 sn-p 中可以看到,

    (...your custom element...) instanceof HTMLUnknownElement
    

    如果你这样做会返回 false(当你不命名空间时它会返回 true)

    我强烈怀疑验证器甚至不会抱怨,因为它在您自己的命名空间中,并且验证器(除非由愚蠢的人编写)会(至少,真的真的真的应该)承认'命名空间的东西'是它对谴责它知之甚少的东西。

    在未来的 HTML 版本中出现新的(以前是自定义的)元素是肯定会发生的事情,与默认命名空间中的元素相比,命名空间元素会更频繁地发生。而“HTML 规范人群”根本不负责,例如,“SVG 规范人群”将在明年或 10 年内做什么。天知道谁会引入哪些新的命名空间并变得普遍。他们知道他们不“负责”,因为他们并不愚蠢。出于这些原因,您可以打赌,当您继续使用它们时,您不会遇到任何严重的问题(例如抛出错误或类似的东西) - 如果您'是第一个。可能发生的最糟糕的事情是,如果您没有为它们编写任何 CSS,它们看起来(未呈现)您希望的方式;无论如何,最重要的用例可能是不可见元素(您可以确定 display:none 将适用于您的自定义元素)和“透明容器”(除非您在某处有“>”,否则它不会影响 CSS 的其余部分在 CSS 中)。从哲学上讲,您所做的非常类似于 jQuery,使用类名可以更好地以某些方式转换文档。 jQuery 这样做绝对没有错,如果有问题的类没有被某些 CSS 引用,那也没有任何区别。以同样的方式,当您使用自定义元素时,绝对没有错。只需使用命名空间版本。这样,您也可以安全地使用以后可能添加到“正确”HTML 中的任何名称,而不会与这些元素以后的工作方式发生任何冲突。

    如果 - 令人惊讶的是 - 某些验证器确实抱怨,您应该做的是继续使用您的自定义元素并放弃该验证器。一个验证者抱怨你如何使用你刚想出的自己的命名空间,就像一个交通警察到你家拜访你并抱怨你使用厕所的方式——扔掉它,明白了吗?

    bucket1 = document.getElementById('bucket1');
    console1 = document.getElementById('console1');
    bucket2 = document.getElementById('bucket2');
    console2 = document.getElementById('console2');
    
    
    chicken = document.createElement('chicken');
    chicken.textContent = 'gaak';
    bucket1.appendChild(chicken);
    console1.appendChild(document.createTextNode([
      chicken instanceof HTMLUnknownElement,
      chicken.namespaceURI,
      chicken.tagName
    ].join('\n')));
    
    rooster = document.createElementNS('myOwnNSwhereIamKing', 'roosterConFuoco');
    rooster.textContent = 'gaakarissimo multo appassionata';
    bucket2.appendChild(rooster);
    console2.appendChild(document.createTextNode([
      rooster instanceof HTMLUnknownElement,
      rooster.namespaceURI,
      rooster.tagName
    ].join('\n')));
    =====chicken=====<br>
    <div id='bucket1'></div>
    <pre id='console1'></pre>
    
    =====rooster=====<br>
    <div id='bucket2'></div>
    <pre id='console2'></pre>

    MDN article

    另外,您已经获得了对 createElementNS 的几乎通用浏览器支持。

    【讨论】:

      【解决方案5】:

      hmmm...刚刚发现如果您使用.createElementNS,则创建的元素没有dataset 属性。您仍然可以使用.setAttribute('data-foo', 'bar'),但.dataset.foo='bar' 会更好。我几乎想在上面对我自己的答案投反对票。无论如何,我特此对浏览器供应商不放入数据集表示不满。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-04-07
        • 2021-10-18
        • 1970-01-01
        • 1970-01-01
        • 2019-06-13
        • 1970-01-01
        • 1970-01-01
        • 2014-11-25
        相关资源
        最近更新 更多