【问题标题】:Custom attributes - Yea or nay?自定义属性 - 是还是不是?
【发布时间】:2010-11-02 18:56:34
【问题描述】:

最近我读到越来越多的人在他们的 HTML 标记中使用自定义属性,主要是为了嵌入一些额外的数据位以在 javascript 代码中使用。

我希望收集一些关于使用自定义属性是否是一种好习惯以及一些替代方法的反馈。

它似乎真的可以简化服务器端和客户端代码,但它也不符合 W3C。

我们应该在我们的网络应用程序中使用自定义 HTML 属性吗?为什么或为什么不?

对于那些认为自定义属性是件好事的人:使用它们时有哪些注意事项?

对于那些认为自定义属性不好的人:您使用哪些替代方法来完成类似的事情?

更新:我最感兴趣的是各种方法背后的推理,以及为什么一种方法比另一种更好。我认为我们都可以想出 4-5 种不同的方法来完成同样的事情。 (隐藏元素、内联脚本、额外类、从 id 解析信息等)。

更新 2: 似乎 HTML 5 data- 属性功能在这里有很多支持(我倾向于同意,它看起来是一个可靠的选择)。到目前为止,我还没有看到太多反驳这个建议的方式。使用这种方法是否有任何问题/陷阱需要担心?还是仅仅是对当前 W3C 规范的“无害”失效?

【问题讨论】:

  • 老实说,我最初的立场是他们不是那么坏的东西,这可能会引起纯粹主义者的争议。不过,我觉得我真的需要坐下来评估所有可用的选项来正确支持这一点,因此需要写一篇长文。
  • 要做到这一点,您可能只需要一些反例[s]:您正在尝试实现什么,使用自定义属性如何方便地做到这一点,以及为什么该解决方案更好 并且不比其他没有自定义属性的解决方案差。
  • @ChrisW 我问主要是出于兴趣,而不是出于某些特定的应用程序。
  • 嗯,有很多选项可以将数据获取到客户端:隐藏输入字段、隐藏定义列表、类、元数据插件、拥有一个包含所有数据映射的巨大 Javascript 字典(对象)分别,自定义属性,数据属性(HTML5)等。我想探索所有这些,考虑它们的优点,它们的缺陷,最后得出一个结论。这篇文章终于让我开始写这篇文章了。 :) 应该在 2010 年之前完成。
  • @Paolo 你不能只说你写了一篇回答这个问题的文章而不给我们链接。不酷。

标签: javascript html xhtml custom-attribute


【解决方案1】:

HTML 5 明确允许以data 开头的自定义属性。因此,例如,<p data-date-changed="Jan 24 5:23 p.m.">Hello</p> 是有效的。由于它受到标准的正式支持,我认为这是自定义属性的最佳选择。而且它不需要您使用 hack 重载其他属性,因此您的 HTML 可以保持语义。

来源:http://www.w3.org/TR/html5/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes

【讨论】:

  • 这是一个很好的方法.. 但我怀疑它是否会工作你必须支持 IE 6 和其他旧浏览器。
  • 我很确定它适用于旧版浏览器;属性被添加到 DOM 中,您可以在其中访问它们。
  • 它在所有使用 HTMLElement 上的 getAttribute() 方法的浏览器上都能很好地工作。另外,随着 HTML5 数据集支持的增长,您可以轻松添加它。
  • @Chuck 显然您可以将属性添加到 DOCTYPE:rodsdot.com/html/… - 我认为这不是一个好主意,但它似乎是标准化的。
  • @Wahnfrieden: w3.org/TR/REC-html40/intro/sgmltut.html#idx-attribute-8 这是经过批准的、符合标准的方法。这里有很好的描述和演示:rodsdot.com/html/… 正如其他人之前发布的那样。
【解决方案2】:

这是我最近一直在使用的一种技术:

<div id="someelement">

    <!-- {
        someRandomData: {a:1,b:2},
        someString: "Foo"
    } -->

    <div>... other regular content...</div>
</div>

评论对象与父元素(即#someelement)相关联。

这是解析器:http://pastie.org/511358

要获取任何特定元素的数据,只需调用 parseData 并将对该元素的引用作为唯一参数传递:

var myElem = document.getElementById('someelement');

var data = parseData( myElem );

data.someRandomData.a; // <= Access the object staight away

它可以比这更简洁:

<li id="foo">
    <!--{specialID:245}-->
    ... content ...
</li>

访问它:

parseData( document.getElementById('foo') ).specialID; // <= 245

使用它的唯一缺点是它不能与自闭合元素一起使用(例如&lt;img/&gt;),因为 cmets 必须在被视为该元素数据的元素


编辑

这种技术的显着好处:

  • 易于实施
  • 不会使 HTML/XHTML 无效
  • 易于使用/理解(基本 JSON 表示法)
  • 与大多数替代方案相比,不显眼且语义更清晰

这是解析器代码(从上面的 http://pastie.org/511358 超链接复制,以防它在 paste.org 上变得不可用):

var parseData = (function(){

    var getAllComments = function(context) {

            var ret = [],
                node = context.firstChild;

            if (!node) { return ret; }

            do {
                if (node.nodeType === 8) {
                    ret[ret.length] = node;
                }
                if (node.nodeType === 1) {
                    ret = ret.concat( getAllComments(node) );
                }
            } while( node = node.nextSibling );

            return ret;

        },
        cache = [0],
        expando = 'data' + +new Date(),
        data = function(node) {

            var cacheIndex = node[expando],
                nextCacheIndex = cache.length;

            if(!cacheIndex) {
                cacheIndex = node[expando] = nextCacheIndex;
                cache[cacheIndex] = {};
            }

            return cache[cacheIndex];

        };

    return function(context) {

        context = context || document.documentElement;

        if ( data(context) && data(context).commentJSON ) {
            return data(context).commentJSON;
        }

        var comments = getAllComments(context),
            len = comments.length,
            comment, cData;

        while (len--) {
            comment = comments[len];
            cData = comment.data.replace(/\n|\r\n/g, '');
            if ( /^\s*?\{.+\}\s*?$/.test(cData) ) {
                try {
                    data(comment.parentNode).commentJSON =
                        (new Function('return ' + cData + ';'))();
                } catch(e) {}
            }
        }

        return data(context).commentJSON || true;

    };

})();

【讨论】:

  • 出于好奇,您使用什么方法制作自闭合标签?我通常需要在 元素上使用类似的东西(以帮助客户端验证规则)。在这种情况下你会采取什么替代方案?
  • 我可能会使用类似的技术,而不是将评论数据绑定到“parentNode”,它可以绑定到评论的“previousSibling”......然后你可以立即获得评论 它将起作用:
  • 有人应该把它做成一个 jquery 插件
  • 评论应该能够被更改/删除而不会破坏任何东西。这就是重点。因此,将任何对标记或代码重要的内容放入 cmets 是一个坏主意。未来的开发者很容易认为它们是 cmets 并删除它们。对于这个问题,我们已经有了一个真正的解决方案:以“data-”为前缀的自定义属性。永远不要使用这种方法。
  • 让我强调@MGOwen 的声明:不要使用 cmets 来添加功能。特别是在 HTML 中。你不使用缩小器吗?在不破坏代码的情况下,您将无法删除 cmets。这也意味着你不能再添加真正的 cmets。
【解决方案3】:

如果您为页面指定架构,则可以创建任何属性。

例如:

Addthis

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:addthis="http://www.addthis.com/help/api-spec">
...
<a addthis:title="" addthis:url="" ...>

Facebook(偶数标签)

<html xmlns:og="http://opengraphprotocol.org/schema/" xmlns:fb="http://www.facebook.com/2008/fbml">
...
<fb:like href="http://developers.facebook.com/" width="450" height="80"/>

【讨论】:

    【解决方案4】:

    避免使用自定义属性的最简单方法是使用现有属性。

    使用有意义的相关类名。
    例如,执行如下操作:type='book'type='cd', 代表书籍和CD。类更适合表示 IS 的内容。

    例如class='book'

    我过去曾使用过自定义属性,但老实说,如果您以语义上有意义的方式使用现有属性,则确实不需要它们。

    举一个更具体的例子,假设您有一个网站提供指向不同类型商店的链接。您可以使用以下内容:

    <a href='wherever.html' id='bookstore12' class='book store'>Molly's books</a>
    <a href='whereverelse.html' id='cdstore3' class='cd store'>James' Music</a>
    

    css 样式可以使用如下类:

    .store { }
    .cd.store { }
    .book.store { }
    

    在上面的示例中,我们看到两个都是指向商店的链接(与网站上其他不相关的链接相反),一个是 cd 商店,另一个是书店。

    【讨论】:

    • 好点,但公平地说,“类型”只在某些标签上有效,当它是有效属性时,它还有一个有效值列表,所以你仍然不是真正的 w3c合规。
    • 我的意思是你不应该为此使用类型标签。因此如果你是......那么你应该......我将编辑以使其更清晰
    • 我倾向于通过在其中一些属性上附加某种类型的“限定符-”来使我的“类”属性具有风味。对于仅与布局相关的 div,我将它的类设置为“layout-xxx”,或者对于围绕重要部分的内部 div,例如书籍或商店,我将有一个 content-book 或 content-store .然后在我的 JavaScript 中,我有一个函数可以根据我要查找的内容在标签上添加这些内容。它有助于我保持整洁有序,但需要一定程度的纪律和预先组织。
    • @Jonathan 双重课效果很好,除非“值”未知。例如,如果它是某种整数 id,我们不能很好地选择每种可能的情况。然后我们需要手动解析类属性,这绝对是可行的,但在代码中并不那么清晰,并且在某些情况下可能会非常慢(如果有很多候选元素要解析)。
    • 遗憾的是,同时为两个类编写 css 选择器(.a.b 注意缺少的空白)在 IE 中不起作用。不过,它确实适用于 Firefox 和其他浏览器。不过,使用类是在标记中嵌入额外语义的好方法
    【解决方案5】:

    在 dom 中嵌入数据并使用metadata 代替jQuery

    所有好的插件都支持元数据插件(允许每个标签选项)。

    它还允许无限复杂的数据/数据结构,以及键值对。

    <li class="someclass {'some': 'random,'json':'data'} anotherclass">...</li>
    

    <li class="someclass" data="{'some':'random', 'json': 'data'}">...</li>
    

    <li class="someclass"><script type="data">{"some":"random","json":"data"}</script> ...</li>
    

    然后像这样获取数据:

    var data = $('li.someclass').metadata();
    if ( data.some && data.some == 'random' )
    alert('It Worked!');
    

    【讨论】:

    • 当有 W3C 批准的指定自定义属性的方式时损坏类属性可能是您被否决的原因。
    • 破坏类属性只是插件使用方法的一种;这不是唯一的方式。
    • 你被否决的另一个原因是建议一个根本不需要插件的插件。
    【解决方案6】:

    我认为在不破坏任何东西或扩展名称空间的情况下使用现有的 XHTML 功能没有问题。我们来看一个小例子:

    <div id="some_content">
     <p>Hi!</p>
    </div>
    

    如何在没有附加属性的情况下向 some_content 添加附加信息?像下面这样添加另一个标签怎么样?

    <div id="some_content">
     <div id="some_content_extended" class="hidden"><p>Some alternative content.</p></div>
     <p>Hi!</p>
    </div>
    

    它通过您选择的明确定义的 id/扩展“_extended”及其在层次结构中的位置来保持关系。我经常将这种方法与 jQuery 一起使用,而实际上并没有使用类似 Ajax 的技术。

    【讨论】:

    • 像这样添加嵌套标签的问题是它往往会创建非常麻烦和丑陋的服务器端代码(JSP/ASP/DTL 等)
    【解决方案7】:

    不。试试这样的:

    <div id="foo"/>
    
    <script type="text/javascript">
      document.getElementById('foo').myProperty = 'W00 H00! I can add JS properties to DOM nodes without using custom attributes!';
    </script>
    

    【讨论】:

    • 所以您更喜欢在整个文档中为动态页面编写大量额外的脚本标签?当在客户端添加信息时,我会使用手动 javascript 分配,但这个问题主要是关于在服务器上呈现什么。此外,jQuery.data() 比你的方法好得多。
    • 上面的答案是一个独立于框架的、用于演示功能的繁琐示例。您可以轻松地扩展它的要点以使代码非常简洁。例如,
      如果您使用的是 jQuery(不是您在原始问题中提到的),请务必使用 data 方法——这就是它的用途。如果不是,那么在架构层之间传递数据是对内联脚本标签的完全有效的使用。
    • 这绝对是一个显而易见的、有效的选择。在我看来,它只是比许多其他不使用自定义属性的替代方案更混乱代码。为了清楚起见,我并不是要好斗或粗鲁,我只是想哄骗您的一些推理,说明您为什么喜欢这种方法。您提供了一个替代方案,但这并不是问题的真正含义。
    • 我不认为这种方法会破坏浏览器。 Microsoft 使用这种确切的机制作为它在 ASP.NET 页面中的首选机制。 (通过在服务器端调用 RegisterExpandoAttribute)。问题似乎集中在客户端而不是服务器上,但在服务器端,所有这些方法都可以(应该?)抽象。
    • 这种方法的优点:--它产生有效的标记(即使在旧的浏览器/规范下)。 --它使数据的意图(由JS消费)明确。 -- 与元素内聚,无需巧妙利用其他特性(如 cmets)。 --不需要特殊解析。从服务器端的角度来看,你可以把它想象成一个 RPC。
    【解决方案8】:

    我没有使用自定义属性,因为我正在输出 XHTML,因为我希望数据能够被第 3 方软件机器读取(不过,如果我愿意,我可以扩展 XHTML 模式)。

    作为自定义属性的替代方案,大多数情况下我发现 id 和 class 属性(例如,在其他答案中提到的)就足够了。

    另外,考虑一下:

    • 如果额外的数据是人类可读的和机器可读的,那么它需要使用(可见的)HTML 标记和文本而不是自定义属性进行编码。

    • 如果它不需要人类可读,那么也许可以使用 invisible HTML 标记和文本对其进行编码。

    有些人例外:他们允许自定义属性,在运行时由客户端的 Javascript 添加到 DOM。他们认为这没问题:因为自定义属性仅在运行时添加到 DOM,所以 HTML 不包含自定义属性。

    【讨论】:

      【解决方案9】:

      我们制作了一个基于网络的编辑器,它可以理解 HTML 的一个子集 - 一个非常严格的子集(邮件客户端几乎可以普遍理解)。我们需要在数据库中表达&lt;td width="@INSWIDTH_42@"&gt; 之类的东西,但我们不能在 DOM 中使用它,否则运行编辑器的浏览器会崩溃(或者更可能会崩溃而不是崩溃)自定义属性)。我们想要拖放,所以把它纯粹放在 DOM 中已经过时了,jquery 的.data() 也是如此(额外的数据没有被正确复制)。我们可能还需要在.html() 中提供额外的数据。最后我们决定在编辑过程中使用&lt;td width="1234" rs-width="@INSWIDTH_42@"&gt;,然后当我们发布所有内容时,我们删除width并进行正则表达式搜索并销毁s/rs-width=/width=/g

      起初,写大部分内容的人是关于这个问题的验证纳粹,并尽一切努力避免我们的自定义属性,但最终在似乎没有其他方法可以满足我们所有要求时默认了。当他意识到自定义属性永远不会出现在电子邮件中时,这很有帮助。我们确实考虑在class 中编码我们的额外数据,但决定这将是两个弊端中的较大者。

      就个人而言,我更喜欢让事情干净并通过验证器等,但作为公司员工,我必须记住,我的主要职责是推进公司的事业(尽可能快地赚钱)可能),而不是我对技术纯度的自负渴望。工具应该为我们工作;他们不是我们。

      【讨论】:

        【解决方案10】:

        我知道人们反对它,但我为此想出了一个超短的解决方案。如果您想使用像“我的”这样的自定义属性,例如:

        <a href="test.html" mine-one="great" mine-two="awesome">Test</a>
        

        然后你可以像 jquery.data() 一样运行这段代码来取回一个对象。

        var custom_props = {} ;
        $.each($(".selector")[0].attributes, function(i,x) {
            if (this.specified && x.name.indexOf("mine-") !== -1) 
                self.new_settings[x.name.replace("modal-","")] = x.value;
        });
        

        【讨论】:

          【解决方案11】:

          规范:创建一个 ASP.NET TextBox 控件,根据属性“DecimalSeparator”和“ThousandsSeparator”,使用 JavaScript 将其文本动态自动格式化为数字。


          将这些属性从控件传输到 JavaScript 的一种方法是让控件呈现自定义属性:

          <input type="text" id="" decimalseparator="." thousandsseparator="," />
          

          自定义属性可以通过 JavaScript 轻松访问。虽然使用具有自定义属性的元素的页面不会validate,但该页面的呈现不会受到影响。


          在我想将字符串和整数等简单类型关联到 HTML 元素以用于 JavaScript 时使用这种方法。如果我想让 HTML 元素更容易识别,我将使用 classid 属性。

          【讨论】:

            【解决方案12】:

            对于复杂的网络应用程序,我会到处放置自定义属性。

            对于更多面向公众的页面,我使用“rel”属性并将我的所有数据转储到 JSON 中,然后使用 MooTools 或 jQuery 对其进行解码:

            <a rel="{color:red, awesome:true, food: tacos}">blah</a>
            

            我最近试图坚持使用 HTML 5 数据属性只是为了“准备”,但它还没有自然而然地出现。

            【讨论】:

              【解决方案13】:

              我一直使用自定义字段,例如

              【讨论】:

              • 这里好像少了一些东西。你的标签完整了吗?
              • 谁能理解这个?请填写您的答案。
              【解决方案14】:

              以我的拙见,不应使用自定义属性,因为它们无法验证。除此之外,您可以为单个元素定义多个类,例如:

              <div class='class1 class2 class3'>
                  Lorem ipsum
              </div>
              

              【讨论】:

              • 就个人而言,我认为这是一个糟糕的例子。你的类名定义了它的外观,而不是它的目的。想想当你想改变所有类似的 div 时……你必须去把它们都改成 span-11 之类的。类应该定义它是什么。样式表应该定义这些东西的外观
              • 您将如何使用这种方法来指定不仅仅是一个标志?我倾向于同意您的立场,并且我不使用自定义属性(尽管我正在考虑)。拥有一个键/值对的优势似乎比简单地添加另一个类要方便得多。
              • @Jonathan Fingland:如果使用 Compass,则无需在此处设置类名。您只需在 .sass 文件中指定它们,您的标记就会很干净。
              • @Jonathan Fingland,class 属性绝对不是仅为“外观”保留的。另一种用途是“用户代理的通用处理”。这说明了规范:w3.org/TR/REC-html40/struct/global.html#h-7.5.2
              • @npup:有趣的引号选择。正如我在一年前所说的那样,样式表定义了这些东西的外观(我将添加 style 属性),而 class 属性用于定义元素的用途。也就是说,它专门用于定义它是什么,而不是它的外观。我认为您可能只是误读了我所说的话,因为据我所知,我们是一致的。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2010-12-11
              • 1970-01-01
              • 2017-01-04
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-10-18
              相关资源
              最近更新 更多