原生 DOM 操作不算复杂,但方法比较多,平时写业务时容易在选择器、返回值和内容更新方式上混在一起。这里记录我自己需要记住的几组常用写法,不追求把所有 DOM API 都列一遍。
一、先记住几个特殊的页面元素
页面最外层的两个元素可以直接通过 document 获取:
document.documentElement; // <html>
document.body; // <body>
这里要注意是 document.documentElement,不是把 document 拼接成其他形式。实际开发中,如果只是查找页面中的普通元素,通常会使用下面几种方法。
二、获取页面元素的几种方式
getElementById:根据 id 获取一个元素,找不到时返回null。querySelector:使用 CSS 选择器获取第一个匹配元素,找不到时也返回null。querySelectorAll:使用 CSS 选择器获取所有匹配元素,返回一个静态的NodeList。getElementsByClassName和getElementsByTagName:较早的集合 API,返回的是动态集合,页面变化后集合内容可能跟着变化。
const app = document.getElementById('app');
const firstItem = document.querySelector('.item');
const items = document.querySelectorAll('.item');
const buttons = document.getElementsByTagName('button');
现在项目里我最常用的是 getElementById 和 querySelector / querySelectorAll。它们的区别可以简单记成:需要唯一 id 时用 getElementById;需要组合条件或 CSS 选择器时用 querySelector。
querySelector 只返回第一个元素,如果要遍历全部结果,就使用 querySelectorAll。它返回的 NodeList 通常可以直接使用 forEach,但不要想当然地把它当成数组;需要数组方法时,可以先转换:
document.querySelectorAll('.item').forEach((item) => {
item.classList.add('active');
});
const itemArray = Array.from(document.querySelectorAll('.item'));
const names = itemArray.map((item) => item.textContent.trim());
另一个容易混淆的点是“静态”和“动态”。querySelectorAll 得到的结果通常是静态的;getElementsByClassName 得到的是动态 HTMLCollection。多数场景下,使用静态结果更容易理解,也不容易因为 DOM 后续变化产生意外。
三、给元素添加内容:优先分清是文本还是 HTML
最常用的三个属性是 textContent、innerText 和 innerHTML:
textContent:读取或设置纯文本,不解析 HTML,适合展示用户输入或普通文本。innerText:读取或设置用户在页面上实际看到的文本,会受到 CSS 显示状态影响。innerHTML:读取或设置 HTML 字符串,会解析标签,适合明确可信的 HTML 片段。
title.textContent = '这是普通文本';
info.innerHTML = '<strong>这是 HTML 内容</strong>';
const text = title.textContent;
const visibleText = title.innerText;
我自己的简单选择原则是:默认使用 textContent;只有内容来源可信、并且确实需要插入标签时才使用 innerHTML。如果把用户输入直接赋给 innerHTML,就可能引入 XSS 风险:
const userInput = '<img src=x onerror=alert(1)>';
box.textContent = userInput; // 只显示字符串,更安全
box.innerHTML = userInput; // 会被当成 HTML 解析,不要直接使用
如果业务确实需要展示用户提供的富文本,不能只靠“过滤几个标签”解决,而应该使用经过验证的 HTML 清洗方案,并结合合适的安全策略。
四、查到元素之后,常见的后续操作
获取元素通常只是第一步,后面经常会修改样式、类名或节点结构:
element.style.color = 'red';
element.classList.add('active');
element.classList.remove('disabled');
element.classList.toggle('dark');
const child = document.createElement('li');
child.textContent = '新内容';
list.appendChild(child);
child.remove();
相比直接拼接字符串,使用 createElement 配合 textContent 创建节点,安全边界更清晰,也更适合处理动态数据。实际写原生 JavaScript 时,我会先用选择器拿到目标元素,再根据内容是否可信决定使用文本、HTML 还是新建节点。