- 文档
- 教程
- 前端
【免费下载链接】zh.javascript.info
现代 JavaScript 教程(The Modern JavaScript Tutorial),以最新的 ECMAScript 规范为基准,通过简单但足够详细的内容,为你讲解从基础到高阶的 JavaScript 相关知识。
当浏览器加载页面时,它会"解析"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>四个要点对应代码中的四个注释:
getAttribute('About')首字母大写也能读到值,因为特性名大小写不敏感;- 任何赋值给特性的内容最终都会变成字符串,
123被存为"123"; - 所有特性,包括我们设置的
Test,都会出现在outerHTML中; 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 属性无法满足需求、且确实需要特性时,才使用特性,典型场景有两个:
- 需要非标准特性——但若它以
data-开头,应改用dataset; - 需要读取 HTML 中"所写的"原始值——因为对应的 DOM 属性可能被浏览器加工过,例如
href属性始终返回完整 URL,而getAttribute('href')才返回原文。
- 文档
- 教程
- 前端
【免费下载链接】zh.javascript.info
现代 JavaScript 教程(The Modern JavaScript Tutorial),以最新的 ECMAScript 规范为基准,通过简单但足够详细的内容,为你讲解从基础到高阶的 JavaScript 相关知识。
相关推荐
Javacord斜杠命令(Slash Commands)实战:现代Discord机器人交互
Javacord斜杠命令 Slash Commands 实战:现代Discord机器人交互 Javacord是一个简单易用的多线程Java库,专为创建Disco
一文读懂Taiwan-LLM-7B-v2.1-chat的训练超参数:学习率5e-05如何影响模型表现
一文读懂Taiwan LLM 7B v2.1 chat的训练超参数:学习率5e 05如何影响模型表现 Taiwan LLM 7B v2.1 chat作为一款专为
《现代 JavaScript 教程》实战:DOM 子节点访问与导航属性全解析
《现代 JavaScript 教程》实战:DOM 子节点访问与导航属性全解析 本文以 en.javascript.info https://link.gitco
文档/教程前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考