MENU

Attributes of HTML and Properties of DOM

2021 年 09 月 14 日 • 阅读: 7286 • 前端

当浏览器加载页面时,它 “读取” (或 “解析”)HTML ,以此生成 DOM 对象。对于元素节点,大多数标准 HTML attributes 自动变成 DOM 对象的 properties。

例如,标签 <body id="page"> 对应的 DOM 对象,有 body.id === "page"

但是 attribute-property 并非一一映射!本节我们将留心区分这两个概念,来看看当它们相同和不同时分别如何处理。

DOM Properties

我们已经见过许多内置 DOM property。但如果还不够,我们可以自己添加,这在技术上完全没有限制。

DOM 节点是常规 JavaScript 对象,我们可以改变它们。

例如,让我们在 document.body 上创建一个新 property

  • document.body.myData = {
  • name: 'Caesar',
  • title: 'Imperator'
  • };
  • alert(document.body.myData.title); // Imperator

我们还可以添加一个方法:

  • document.body.sayTagName = function() {
  • alert(this.tagName);
  • };
  • document.body.sayTagName(); // BODY (the value of "this" in the method is document.body)

我们甚至可以修改内置的 prototypes,比如 Element.prototype,来为所有元素添加新方法:

  • Element.prototype.sayHi = function() {
  • alert(`Hello, I'm ${this.tagName}`);
  • };
  • document.documentElement.sayHi(); // Hello, I'm HTML
  • document.body.sayHi(); // Hello, I'm BODY

所以,DOM properties 和 methods 表现得就像那些常规 JavaScript 对象:

  • 它们可以有任何值
  • 它们是大小写敏感的(elem.nodeTypeelem.NoDeTyPe 不同)

HTML Attributes

HTML 标签可能包含 attributes。当浏览器通过解析它去创建 DOM 对象时,会识别 standard attributes 并以此创建 DOM properties。

所以当元素有 id 和另一个 standard attribute 时,相应的 property 就会被创建。但如果 attribute 是 non-standard,这种情况不会发生。

比如:

  • <body id="test" something="non-standard">
  • <script>
  • alert(document.body.id); // test
  • // non-standard attribute does not yield a property
  • alert(document.body.something); // undefined
  • </script>
  • </body>

请注意一种元素的 standard attribute 对于另一种可能是未知的。例如,"type"<input> HTMLInputElement 的标准属性,但对于 <body> HTMLBodyElement 并非如此。Standard attributes 声明在相应元素类的规范中。

让我们观察以下代码:

  • <body id="body" type="...">
  • <input id="input" type="text">
  • <script>
  • alert(input.type); // text
  • alert(body.type); // undefined: DOM property not created, because it's non-standard
  • </script>
  • </body>

所以,如果一个 attribute 是 non-standard 的,就不会有对应的 DOM-property。那还能访问这些 attributes 吗?

当然,所有 attributes 都可通过以下方法访问:

  • elem.hasAttribute(name) – 检测是否存在
  • elem.getAttribute(name) – 获取值
  • elem.setAttribute(name, value) – 设置值
  • elem.removeAttribute(name) – 移除 attribute

这些方法操作是和 HTML 内容同步的。

此外可以通过 elem.attributes 读取所有 attributes:一个对象集合,属于内置 Attr 类,有 namevalue properties。

下面是一个读取 non-standard property 的示例:

  • <body something="non-standard">
  • <script>
  • alert(document.body.getAttribute('something')); // non-standard
  • </script>
  • </body>

HTML attributes 有以下特点:

  • 它们的名称大小写不敏感(idID 相同)
  • 它们的值总是字符串

下面是一个处理 attributes 的拓展示例:

  • <body>
  • <div id="elem" about="Elephant"></div>
  • <script>
  • alert( elem.getAttribute('About') ); // (1) 'Elephant', reading
  • elem.setAttribute('Test', 123); // (2), writing
  • alert( elem.outerHTML ); // (3), see if the attribute is in HTML (yes)
  • for (let attr of elem.attributes) { // (4) list all
  • alert( `${attr.name} = ${attr.value}` );
  • }
  • </script>
  • </body>

请注意:

  1. getAttribute('About') - 首字母是大写,但 HTML 中都是小写。但那不重要:attribute 名大小写不敏感
  2. 我们可以给 attribute 赋任何值,但它会变成一个字符串。所以这儿有值 "123"
  3. 所有 attributes,包括我们设置的那个,都会出现在 outerHTML
  4. attributes 集合是可迭代的,它包含元素的所有 attributes(standard and non-standard)构成的对象,这些对象有 namevalue properties

Property-attribute 同步

当 standard attribute 改变时,相应的 property 会自动更新,反之亦然(有例外情况)。

下面的示例中,id 被作为 attribute 修改,我们可以看到 property 也会跟着改变。反过来也一样:

  • <input>
  • <script>
  • let input = document.querySelector('input');
  • // attribute => property
  • input.setAttribute('id', 'id');
  • alert(input.id); // id (updated)
  • // property => attribute
  • input.id = 'newId';
  • alert(input.getAttribute('id')); // newId (updated)
  • </script>

但也有例外,比如 input.value 只会从 attribute 同步到 property,反过来不行:

  • <input>
  • <script>
  • let input = document.querySelector('input');
  • // attribute => property
  • input.setAttribute('value', 'text');
  • alert(input.value); // text
  • // NOT property => attribute
  • input.value = 'newValue';
  • alert(input.getAttribute('value')); // text (not updated!)
  • </script>

上例中:

  • 改变 value attribute 会更新 property
  • 但 property 改变不影响 attribute

这个 “特性” 也许真的会派上用场,因为用户动作会导致 value 改变,在那之后,如果我们想从 HTML 恢复 "原始" 值,它就在 attribute 里。

DOM properties 是有类型的

DOM properties 并不总是字符串。例如,input.checked property (checkbox 对应 DOM 对象的 property)是一个 boolean:

  • <input id="input" type="checkbox" checked> checkbox
  • <script>
  • alert(input.getAttribute('checked')); // the attribute value is: empty string
  • alert(input.checked); // the property value is: true
  • </script>

另一个例子,style attribute 是字符串,但 style property 是一个对象:

  • <div id="div" style="color:red;font-size:120%">Hello</div>
  • <script>
  • // string
  • alert(div.getAttribute('style')); // color:red;font-size:120%
  • // object
  • alert(div.style); // [object CSSStyleDeclaration]
  • alert(div.style.color); // red
  • </script>

但大多数 properties 是字符串。

还有非常少见的情况,即使 DOM property 的类型是字符串,它也可能与 attribute 不同。比如,DOM property 的 href 总是一个 full URL,即便 attribute 只包含一个相对 URL,甚至是一个 #hash

见下例:

  • <a id="a" href="#hello">link</a>
  • <script>
  • // attribute
  • alert(a.getAttribute('href')); // #hello
  • // property
  • alert(a.href ); // full URL in the form http://site.com/page#hello
  • </script>

如果我们需要 href 或其它 attribute 在 HTML 中的原始值,可以使用 getAttribute

Non-standard attributes, dataset

编写 HTML 时,我们会使用很多 standard attributes。但可以使用 non-standard 或自定义 attribute 吗?让我们先看看它们是否有用,能用在哪里。

有时,non-standard attributes 用来将自定义数据从 HTML 传递到 JavaScript,或者为 JavaScript “标记” HTML。

像这样:

  • <!-- mark the div to show "name" here -->
  • <div show-info="name"></div>
  • <!-- and age here -->
  • <div show-info="age"></div>
  • <script>
  • // the code finds an element with the mark and shows what's requested
  • let user = {
  • name: "Pete",
  • age: 25
  • };
  • for(let div of document.querySelectorAll('[show-info]')) {
  • // insert the corresponding info into the field
  • let field = div.getAttribute('show-info');
  • div.innerHTML = user[field]; // first Pete into "name", then 25 into "age"
  • }
  • </script>

它们也被用来设置元素样式。

例如,这里使用 order-state attribute 设置订单状态样式:

  • <style>
  • /* styles rely on the custom attribute "order-state" */
  • .order[order-state="new"] {
  • color: green;
  • }
  • .order[order-state="pending"] {
  • color: blue;
  • }
  • .order[order-state="canceled"] {
  • color: red;
  • }
  • </style>
  • <div class="order" order-state="new">
  • A new order.
  • </div>
  • <div class="order" order-state="pending">
  • A pending order.
  • </div>
  • <div class="order" order-state="canceled">
  • A canceled order.
  • </div>

为何倾向于使用单个 attribute,而非像 .order-state-new, .order-state-pending, .order-state-canceled 这样的类。

因为单个属性更加方便管理,状态可以很容易地改变:

  • // a bit simpler than removing old/adding a new class
  • div.setAttribute('order-state', 'canceled');

但自定义 attribute 可能存在一个问题。如果我们使用 non-standard attribute 实现功能,今后标准引入了它们,并赋予了它们一些行为,这会怎么样呢?HTML 语言是活跃的,它持续成长,越来越多的 attributes 出现来满足开发者需求。这可能造成意想不到的影响。

为了避免冲突,存在 data-* 属性。

所有以 “data-” 开头的 attributes 被保留给开发者使用。它们可以通过 dataset property 访问

例如,如果一个 elem 包含名为 data-about 的 attribute,它可以通过 elem.dataset.about 获取。

像这样:

  • <body data-about="Elephants">
  • <script>
  • alert(document.body.dataset.about); // Elephants
  • </script>

data-order-state 这样的多单词 attribute 会变成 camel-cased: dataset.orderState

下面是一个重写的 “订单状态” 示例:

  • <style>
  • .order[data-order-state="new"] {
  • color: green;
  • }
  • .order[data-order-state="pending"] {
  • color: blue;
  • }
  • .order[data-order-state="canceled"] {
  • color: red;
  • }
  • </style>
  • <div id="order" class="order" data-order-state="new">
  • A new order.
  • </div>
  • <script>
  • // read
  • alert(order.dataset.orderState); // new
  • // modify
  • order.dataset.orderState = "pending"; // (*)
  • </script>

使用 data-* attributes 是一个合法且安全的自定义数据传递方式。

请注意我们不仅可以读取,也能修改 data-attributes。这样 CSS 就可以根据值更新视图:上例最后一行 (*) 将颜色改为蓝色。

Summary

  • Attributes – 是写在 HTML 里的内容
  • Properties – 是 DOM 对象中的属性

一个不完整比较:

PropertiesAttributes
类型任何值,standard properties 的类型描述在 spec 中 string
名称大小写敏感大小写不敏感

处理 attributes 的方法是:

  • elem.hasAttribute(name) – 检测存在与否
  • elem.getAttribute(name) – 获取值
  • elem.setAttribute(name, value) – 设置值
  • elem.removeAttribute(name) – 移除 attribute
  • elem.attributes 是所有 attributes 的集合

对于大多数情况,更倾向于使用 DOM properties,除非需要精确的 attributes,例如:

  • non-standard attribute。但如果它以 data- 开始,我们应该使用 dataset
  • 读取写在 HTML 中的值。DOM property 的值可能有所不同,例如 href property 总是完整 URL,但我们可能想获得 “原始” 值。

Tasks

Get the attribute

编写代码选择包含 data-widget-name attribute 的元素,并读取它的值。

  • <!DOCTYPE html>
  • <html>
  • <body>
  • <div data-widget-name="menu">Choose the genre</div>
  • <script>
  • // getting it
  • let elem = document.querySelector('[data-widget-name]');
  • // reading the value
  • alert(elem.dataset.widgetName);
  • // or
  • alert(elem.getAttribute('data-widget-name'));
  • </script>
  • </body>
  • </html>

Make external links orange

通过 style property 让外链变成橘黄色。

一个链接是外部的,如果:

  • 它的 href 包含 ://
  • 但不以 http://internal.com 开头
  • <a name="list">the list</a>
  • <ul>
  • <li><a href="http://google.com">http://google.com</a></li>
  • <li><a href="/tutorial">/tutorial.html</a></li>
  • <li><a href="local/path">local/path</a></li>
  • <li><a href="ftp://ftp.com/my.zip">ftp://ftp.com/my.zip</a></li>
  • <li><a href="http://nodejs.org">http://nodejs.org</a></li>
  • <li><a href="http://internal.com/test">http://internal.com/test</a></li>
  • </ul>
  • <script>
  • let links = document.querySelectorAll('a');
  • for (let link of links) {
  • let href = link.getAttribute('href');
  • if (!href) continue; // no attribute
  • if (!href.includes('://')) continue; // no protocol
  • if (href.startsWith('http://internal.com')) continue; // internal
  • link.style.color = 'orange';
  • }
  • </script>

另一种方法:

  • // look for all links that have :// in href
  • // but href doesn't start with http://internal.com
  • let selector = 'a[href*="://"]:not([href^="http://internal.com"])';
  • let links = document.querySelectorAll(selector);
  • links.forEach(link => link.style.color = 'orange');
本文译自 Attributes and properties,译者 LOGI
TG 大佬群 QQ 大佬群

返回文章列表 文章二维码
本页链接的二维码
打赏二维码
添加新评论

Loading captcha...

已有 1 条评论
  1. 强调几点:(该留言由系统自动生成!)
    1. 请不要刷广告,本站没有流量!
    2. 我不回复虚假邮箱,因为回复了你也看不到!
    3. 存在必须回复的隐藏内容时,可以直接使用表情框里的阿鲁表情!

  1. Uncle Uncle   Windows 10 x64 Edition  Google Chrome 99.0.4844.51

    好家伙又学到了捏 @(滑稽)