
2.2 DOM 元素内容与属性操作
元素内容与属性操作是 DOM 视图动态更新的核心手段,通过 JavaScript 可以实时修改页面的文本内容、HTML 结构与标签属性,实现数据渲染、状态切换、交互反馈等核心交互效果。
不同的内容操作方式在安全性、性能、语义上存在显著差异,理解其边界与适用场景,是规避 XSS 风险、性能损耗与兼容性问题的基础。
2.2.1 内容读写操作(innerHTML/innerText/textContent)
JavaScript 提供了三种常用的元素内容读写属性,分别面向 HTML 结构、可见文本、全量文本三种场景,三者在读取范围、写入行为、安全性能上差异明显。
一、innerHTML
innerHTML 用于读写元素内部的完整 HTML 代码。读取时返回元素内所有 HTML 标记字符串;
写入时会将字符串作为 HTML 解析,生成新的 DOM 节点替换原有内容。
const box = document.querySelector('.box');
// 读取 HTML 结构
console.log(box.innerHTML);
// 写入 HTML 结构,浏览器会自动解析标签生成 DOM
box.innerHTML = '<p>这是一段<strong>加粗</strong>文本</p>';
- 核心特点:
- 支持渲染完整 HTML 结构,是富文本、模板渲染的核心方式
- 写入时会销毁原有子节点及其事件绑定,完整重建 DOM 子树
- 高安全风险:直接插入未过滤的用户输入会导致 XSS 攻击
- 性能开销大:大量 HTML 写入会触发完整的解析、重排与重绘
二、innerText
innerText 用于读写元素的可见文本内容,返回结果与用户肉眼在页面上看到的文本一致,受 CSS 样式影响。
const box = document.querySelector('.box');
console.log(box.innerText); // 仅返回页面可见的文本
box.innerText = '<p>纯文本</p>'; // 会作为纯文本显示,不会解析标签
- 核心特点:
- 写入时作为纯文本处理,不会解析 HTML 标签,天然防御 XSS
- 读取时会触发 CSS 样式计算(重排),性能较差
- 自动忽略 display: none 的隐藏元素、<script>/<style> 内的文本
- 会保留换行与基本排版格式,输出更贴近视觉效果
三、textContent
textContent 用于读写元素内的全部文本内容,包含所有子节点的文本,不考虑 CSS 样式与元素可见性。
const box = document.querySelector('.box');
console.log(box.textContent); // 返回所有文本,包括隐藏元素的内容
box.textContent = '纯文本内容'; // 纯文本写入,不解析标签
- 核心特点:
- 写入时纯文本处理,无 XSS 安全风险
- 读取时不触发样式计算,性能远优于 innerText
- 会获取所有文本节点,包括隐藏元素、注释外的所有文本内容
- 不会自动处理排版格式,仅保留原始文本的换行符
四、三者对比与选型
|
对比维度 |
innerHTML |
innerText |
textContent |
|
读取内容 |
完整 HTML 代码,包含标签 |
仅可见文本,受 CSS 影响 |
全部文本内容,含隐藏文本 |
|
写入行为 |
解析 HTML 标签,重建 DOM |
纯文本渲染,不解析标签 |
纯文本渲染,不解析标签 |
|
安全性 |
存在 XSS 注入风险 |
安全,无 XSS 风险 |
安全,无 XSS 风险 |
|
性能表现 |
差,触发完整解析与重排 |
较差,读取触发样式重排 |
好,仅操作文本节点 |
|
适用场景 |
可信 HTML 结构渲染 |
需要匹配视觉可见文本的场景 |
纯文本读写、性能优先场景 |
五、最佳实践
- 纯文本更新优先使用 textContent,性能最优且安全
- 仅在渲染可信 HTML 内容时使用 innerHTML,用户输入内容必须先做 XSS 过滤
- 避免频繁调用 innerHTML 做小幅修改,优先操作具体节点的属性与文本
- 大量内容渲染避免字符串拼接式 innerHTML,推荐使用 DocumentFragment 批量操作
2.2.2 原生属性与自定义属性操作
HTML 元素的属性分为原生标准属性(如 id、class、src)和自定义属性两类,JavaScript 提供了两套操作方式:DOM 对象属性直接访问、Attribute 系列方法,二者在行为上存在关键差异。
一、DOM 对象属性直接访问(点语法)
通过 element.属性名 直接读写 DOM 对象的属性,仅支持原生标准属性,操作的是内存中 DOM 对象的属性值,而非 HTML 标签上的原始值。
const input = document.querySelector('input');
// 读取原生属性
console.log(input.id);
console.log(input.value);
// 修改原生属性
input.className = 'active';
input.disabled = true;
- 核心特点:
- 仅支持标准原生属性,无法直接操作非标准自定义属性
- 返回值是经过浏览器处理的当前状态值,而非标签原始字符串
- 例如 img.src 返回完整绝对路径,而非标签上的相对路径
- 布尔属性(disabled、checked)返回真正的布尔值 true/false
- 直接操作内存对象,读写性能更高
二、Attribute 系列方法
通过 getAttribute()、setAttribute()、removeAttribute()、hasAttribute() 方法操作 HTML 标签上的属性,支持所有属性包括自定义属性,操作的是标签上的原始字符串值。
const link = document.querySelector('a');
// 获取属性值,返回标签上的原始值
console.log(link.getAttribute('href')); // 返回相对路径字符串
// 设置属性
link.setAttribute('target', '_blank');
// 判断属性是否存在
console.log(link.hasAttribute('title'));
// 删除属性
link.removeAttribute('custom-attr');
- 核心特点:
- 支持所有属性,包括非标准自定义属性
- 返回值始终是字符串或 null,布尔属性返回属性名本身或 null
- 操作会直接修改 HTML 标签结构,变化可反映在 outerHTML 中
- 性能略低于 DOM 对象直接属性访问
三、核心差异对比
|
对比项 |
DOM 对象属性(点语法) |
Attribute 系列方法 |
|
支持范围 |
仅原生标准属性 |
所有属性,含自定义属性 |
|
返回值类型 |
对应属性的实际类型(布尔、数字等) |
始终为字符串 /null |
|
值内容 |
浏览器计算后的当前值(如绝对路径) |
标签上的原始字符串值 |
|
布尔属性 |
返回 true /false 布尔值 |
返回属性名 /null |
|
读写性能 |
高,直接操作内存 |
略低,操作标签结构 |
四、常见坑点
2.2.3 data-* 数据集规范
data-* 是 HTML5 标准化的自定义属性方案,通过统一的 data- 前缀定义自定义属性,既符合标准规范,又避免与未来原生属性冲突,同时配套了专属的 dataset API,是元素关联业务数据的推荐方式。
一、基础语法与命名规则
- 在 HTML 标签中以 data- 为前缀,后面跟自定义属性名,多单词命名使用横杠分隔
- DOM 中对应 dataset 属性,是一个 DOMStringMap 对象,自动将横杠命名转换为驼峰命名
示例:
<!– HTML 中定义 data-* 属性 –>
<li data-id="1001" data-user-name="张三" data-status="active">列表项</li>
const li = document.querySelector('li');
// 通过 dataset 读取,横杠自动转为驼峰
console.log(li.dataset.id); // "1001"
console.log(li.dataset.userName); // "张三"
// 设置属性,同步更新 HTML 标签
li.dataset.status = 'disabled';
// 删除属性
delete li.dataset.status;
二、核心特点
三、典型应用场景
const list = document.querySelector('.list');
list.addEventListener('click', (e) => {
const item = e.target.closest('li');
if (item) {
const id = item.dataset.id;
console.log('点击了项:', id);
}
});
四、注意事项
- 仅适合存储少量简单数据,大量复杂数据建议使用 Map 与元素关联存储
- 所有值均为字符串类型,读取后需根据业务做显式类型转换
- 避免存储敏感信息,data-* 属性会直接暴露在 HTML 源码中
- 严格遵循横杠转驼峰命名规则,不要在 dataset 中使用大写字母直接命名





