☰
DOM 特性(Attributes)与属性(Properties)完全指南:现代 JavaScript 教程中的同步机制与 dataset 实战
2026/10/8 7:49:29 网站建设 项目流程
  • 文档
  • 教程
  • 前端

【免费下载链接】zh.javascript.info

现代 JavaScript 教程(The Modern JavaScript Tutorial),以最新的 ECMAScript 规范为基准,通过简单但足够详细的内容,为你讲解从基础到高阶的 JavaScript 相关知识。

项目地址:https://gitcode.com/gh_mirrors/zh/zh.javascript.info
点击查看免费下载

当浏览器加载页面时,它会"解析"HTML 并从中生成 DOM 对象:对于元素节点,大多数标准的 HTML 特性(attributes)会自动变成 DOM 对象的属性(properties)。然而,特性与属性的映射并不是一一对应的——它们既有双向同步的默契,也有"特性单向影响属性"的例外,更有href返回完整 URL 这类"属性值被浏览器加工过"的陷阱。本篇以现代 JavaScript 教程(zh.javascript.info)的 DOM 特性与属性章节 为核心,结合仓库中的配套练习与可运行示例,系统讲清两者的区别、操作 API、同步规则,以及用data-*特性安全传递自定义数据的最佳实践,让你读完能够精准地在"该用属性还是该用特性"之间做出正确选择。

先从概念上分清:特性写在 HTML,属性活在 DOM

在 HTML 中,标签可以拥有特性(attribute);当浏览器解析 HTML 文本并根据标签创建 DOM 对象时,浏览器会辨别标准的特性并据此创建 DOM 属性(property)。

例如,如果标签是<body id="page">,那么 DOM 对象就会有body.id = "page"。但这条规则只对标准特性生效:非标准的特性不会生成对应的 DOM 属性。来看一个直观的例子:

<body id="test" something="non-standard"> <script> alert(document.body.id); // test —— 标准特性 id 生成了属性 // 非标准的特性没有获得对应的属性 alert(document.body.something); // undefined </script> </body>

注意,一个元素的标准特性对于另一个元素可能是未知的。例如"type"是<input>(对应HTMLInputElement类)的标准特性,但对<body>(对应HTMLBodyElement类)来说则不是——规范在相应元素类中详细描述了各自的标准属性。所以:

<body id="body" type="..."> <input id="input" type="text"> <script> alert(input.type); // text alert(body.type); // undefined:DOM 属性没有被创建,因为它不是一个标准的特性 </script> </body>

在 DOM 节点类 中我们可以找到这种映射的类层次根源:每个 DOM 节点都属于相应的内建类(如HTMLInputElement、HTMLBodyElement、HTMLAnchorElement等),而"哪些特性是标准的、会生成哪种类型的属性"正是由这些元素类定义的。简言之:

  • 特性(attribute)——写在 HTML 中的内容;
  • 属性(property)——DOM 对象中的内容。

DOM 属性:就是普通 JavaScript 对象

DOM 节点本质上是常规的 JavaScript 对象,因此我们可以像操作任何对象一样操作它们——这也是理解后续所有内容的前提。

给 DOM 对象添加自定义属性:

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

给 DOM 对象添加方法(此时方法中的this就是该元素):

document.body.sayTagName = function() { alert(this.tagName); }; document.body.sayTagName(); // BODY

修改内建属性的原型,为所有元素统一添加方法:

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 属性和方法的行为与常规 JavaScript 对象完全一致:它们可以承载任意类型的值;它们的名字是大小写敏感的(要写成elem.nodeType,而不是elem.NoDeTyPe)。

HTML 特性:四个 API 与一个集合

既然非标准特性不会自动映射为 DOM 属性,那如何访问它们?所有特性都可以通过下面四个方法访问,它们操作的实际上是 HTML 中的原始内容:

方法作用
elem.hasAttribute(name)检查特性是否存在
elem.getAttribute(name)获取特性值
elem.setAttribute(name, value)设置特性值
elem.removeAttribute(name)移除特性

此外,elem.attributes可以读取所有特性——它是属于内建Attr类的对象的集合,每个对象具有name和value属性。

读取非标准特性的示例:

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

HTML 特性有两个重要特征:

  • 它们的名字是大小写不敏感的(id与ID相同);
  • 它们的值总是字符串类型。

下面是一个使用特性的扩展示例,涵盖读取、写入、查看与遍历:

<body> <div id="elem" about="Elephant"></div> <script> alert( elem.getAttribute('About') ); // (1) 'Elephant',读取(大写 A 也可以) elem.setAttribute('Test', 123); // (2) 写入,数值会被转为字符串 "123" alert( elem.outerHTML ); // (3) 所有特性(包括刚设置的)都在 HTML 中可见 for (let attr of elem.attributes) { // (4) 遍历所有特性 alert( `${attr.name} = ${attr.value}` ); } </script> </body>

四个要点对应代码中的四个注释:

  1. getAttribute('About')首字母大写也能读到值,因为特性名大小写不敏感;
  2. 任何赋值给特性的内容最终都会变成字符串,123被存为"123";
  3. 所有特性,包括我们设置的Test,都会出现在outerHTML中;
  4. attributes集合是可迭代对象,包含元素全部特性(标准与非标准),以name/value形式存放。

属性—特性同步:哪些双向,哪些单向

当一个标准特性被改变,对应的属性也会自动更新,反之亦然——除了几个特例。

双向同步的典型:id

<input> <script> let input = document.querySelector('input'); // 特性 => 属性 input.setAttribute('id', 'id'); alert(input.id); // id(属性被更新了) // 属性 => 特性 input.id = 'newId'; alert(input.getAttribute('id')); // newId(特性被更新了) </script>

单向同步的例外:input.value只能从特性同步到属性,反过来不行

<input> <script> let input = document.querySelector('input'); // 特性 => 属性 input.setAttribute('value', 'text'); alert(input.value); // text // 这个操作无效,属性 => 特性 input.value = 'newValue'; alert(input.getAttribute('value')); // text(没有被更新!) </script>

这个"单向"特性在实际开发中非常有用:用户输入会改变value属性(比如用户在输入框中敲入了内容),但 HTML 中的value特性仍然保留着"原始"值。因此当我们需要在用户操作之后恢复初始内容时,直接从特性中读取即可。

DOM 属性是多类型的:布尔、对象与"加工过的"URL

DOM 属性并不总是字符串。这与特性"值永远是字符串"形成鲜明对比。

布尔型:input.checked

<input id="input" type="checkbox" checked> checkbox <script> alert(input.getAttribute('checked')); // 特性值是:空字符串 alert(input.checked); // 属性值是:true </script>

对象型:style特性是字符串,style属性是对象

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

字符串但内容被加工:href属性永远是完整 URL

即使特性里写的是相对路径或#hash,hrefDOM 属性也会返回完整的绝对 URL:

<a id="a" href="#hello">link</a> <script> alert(a.getAttribute('href')); // #hello(原始值) alert(a.href); // http://site.com/page#hello 形式的完整 URL </script>

这个差异在实战中极其关键——如果你需要"与 HTML 中所写的完全一致"的值,必须使用getAttribute。仓库中 黄色链接练习的题解 就专门强调了这一点(详见下文实战部分)。

非标准特性的价值与风险

非标准、自定义的特性常被用来将自定义数据从 HTML 传递到 JavaScript,或为 JavaScript"标记"HTML 元素。

用法一:作为数据标记,驱动页面渲染

<!-- 标记这个 div 以在这显示 "name" --> <div show-info="name"></div> <!-- 标记这个 div 以在这显示 "age" --> <div show-info="age"></div> <script> let user = { name: "Pete", age: 25 }; for (let div of document.querySelectorAll('[show-info]')) { let field = div.getAttribute('show-info'); // 读取标记名 div.innerHTML = user[field]; // 首先 "name" 变为 Pete,然后 "age" 变为 25 } </script>

用法二:作为状态标记,配合 CSS 选择器控制样式

<style> /* 样式依赖于自定义特性 "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>

为什么用特性比用一堆样式类(.order-state-new、.order-state-pending……)更好?因为状态切换只需一行赋值,比"删除旧类、添加新类"简单得多:

div.setAttribute('order-state', 'canceled');

风险在于:如果我们出于自己的目的使用了非标准特性,之后它被引入标准并有了自己的用途,自定义属性就可能产生意料不到的副作用。HTML 语言在不断演进,越来越多的特性进入标准。为了避免冲突,规范提供了data-*特性——所有以data-开头的特性均被保留供程序员使用,它们可以在dataset属性中访问。

data-* 与 dataset:传递自定义数据的合法安全方式

所有以data-开头的特性均被保留供程序员使用。它们可在dataset属性中使用。

如果elem有一个名为data-about的特性,就可以通过elem.dataset.about读取:

<body><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"><div>// 获取元素 let elem = document.querySelector('[data-widget-name]'); // 读取值 alert(elem.dataset.widgetName); // menu // 或 alert(elem.getAttribute('data-widget-name')); // menu

练习二:将外部链接设为橙色(importance 3)

任务描述 要求通过修改style属性,把所有"外部"链接变成橙色。判定条件:链接href中包含://,但不是以http://internal.com开头。

题解 给出了两种方案。方案一:先用querySelectorAll('a')拿到所有链接再逐个过滤,并特别强调必须用link.getAttribute('href')而不是link.href——因为我们需要的是 HTML 中的原始值,而href属性返回的是加工后的完整 URL:

let links = document.querySelectorAll('a'); for (let link of links) { let href = link.getAttribute('href'); // 原始值,而非 link.href if (!href) continue; // 没有 href 特性 if (!href.includes('://')) continue; // 没有协议 if (href.startsWith('http://internal.com')) continue; // 内部链接 link.style.color = 'orange'; }

方案二更简洁:直接用 CSS 选择器完成筛选,[href*="://"]匹配包含://的 href,:not([href^="http://internal.com"])排除内部链接:

let selector = 'a[href*="://"]:not([href^="http://internal.com"])'; let links = document.querySelectorAll(selector); links.forEach(link => link.style.color = 'orange');

仓库中的可运行示例 source.view 与 solution.view 分别对应"待完成版本"与"已解决版本",后者完整展示了用 CSS 选择器方案实现的效果——http://google.com、ftp://ftp.com/my.zip、http://nodejs.org变橙,而/tutorial.html、local/path(无://)和http://internal.com/test(内部链接)保持原样。

总结:什么时候用属性,什么时候用特性

属性(property)特性(attribute)
类型任何值,标准的属性具有规范中描述的类型字符串
名字名字(name)是大小写敏感的名字(name)是大小写不敏感的

操作特性的方法速查:

  • elem.hasAttribute(name)—— 检查是否存在这个特性;
  • elem.getAttribute(name)—— 获取这个特性值;
  • elem.setAttribute(name, value)—— 设置这个特性值;
  • elem.removeAttribute(name)—— 移除这个特性;
  • elem.attributes—— 所有特性的集合(可迭代,元素含name/value)。

在大多数情况下,优先使用 DOM 属性。仅当 DOM 属性无法满足需求、且确实需要特性时,才使用特性,典型场景有两个:

  1. 需要非标准特性——但若它以data-开头,应改用dataset;
  2. 需要读取 HTML 中"所写的"原始值——因为对应的 DOM 属性可能被浏览器加工过,例如href属性始终返回完整 URL,而getAttribute('href')才返回原文。
  • 文档
  • 教程
  • 前端

【免费下载链接】zh.javascript.info

现代 JavaScript 教程(The Modern JavaScript Tutorial),以最新的 ECMAScript 规范为基准,通过简单但足够详细的内容,为你讲解从基础到高阶的 JavaScript 相关知识。

项目地址:https://gitcode.com/gh_mirrors/zh/zh.javascript.info
点击查看免费下载
上一篇:如何在Apple Silicon上快速部署mlx-community/DeepSeek-V4-Pro-Qwen3.5-4B-bf16:零基础入门教程
下一篇:如何快速构建250+格式的本地WebAssembly文件转换器:VERT开源项目终极指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询