DOM (文档对象模型)

你可以把网页想象成一棵树,HTML 是树的根,<html><head><body> 是它的主要分支,而每一个标签(如 <p>, <div>, <span>, <a>)都是一个节点或树叶。

JavaScript 可以通过这棵“树”来找到任何一个你想要的“树叶”(元素),然后读取它的信息(如文本内容、属性值、样式等)或修改它。


获取单个元素

当你只想找一个特定的元素时,可以使用以下方法。

getElementById()

这是最快、最直接的方法,但前提是元素必须有唯一的 id 属性。

<div id="main-title">欢迎来到我的网站</div>
<script>
  // 通过ID获取元素
  const titleElement = document.getElementById('main-title');
  // 获取元素的文本内容
  const titleText = titleElement.textContent; // 或 innerText
  console.log(titleText); // 输出: "欢迎来到我的网站"
  // 获取元素的标签名
  const tagName = titleElement.tagName;
  console.log(tagName); // 输出: "DIV"
</script>

querySelector()

这是一个非常强大和现代的方法,它使用 CSS 选择器 来查找元素,你可以用它通过 idclass、标签名或任何复杂的 CSS 选择器来找到第一个匹配的元素。

<p class="highlight">这是一个重要的段落。</p>
<a href="https://example.com">这是一个链接</a>
<script>
  // 通过 class 选择器 (注意加点 .)
  const highlightedPara = document.querySelector('.highlight');
  console.log(highlightedPara.textContent); // 输出: "这是一个重要的段落。"
  // 通过标签名选择器
  const firstLink = document.querySelector('a');
  console.log(firstLink.href); // 输出: "https://example.com"
  // 通过属性选择器
  const linkWithHref = document.querySelector('a[href]');
  console.log(linkWithHref.textContent); // 输出: "这是一个链接"
</script>

获取多个元素

当你需要获取一组符合某个条件的元素时(所有的 <p> 标签或所有 class="item" 的元素),可以使用以下方法,这些方法返回的是一个 NodeList(类数组对象),你可以像数组一样遍历它。

getElementsByClassName()

根据 class 名获取元素集合。

<div class="item">项目 1</div>
<div class="item">项目 2</div>
<div class="item">项目 3</div>
<script>
  // 获取所有 class 为 "item" 的元素
  const items = document.getElementsByClassName('item');
  // items 是一个 HTMLCollection,可以像数组一样遍历
  for (let i = 0; i < items.length; i++) {
    console.log(items[i].textContent);
  }
  // 或者使用 for...of 循环 (更推荐)
  for (const item of items) {
    console.log(item.textContent);
  }
</script>

getElementsByTagName()

根据标签名获取元素集合。

<p>第一段文本。</p>
<p>第二段文本。</p>
<script>
  // 获取所有的 <p> 标签
  const paragraphs = document.getElementsByTagName('p');
  for (const p of paragraphs) {
    console.log(p.textContent);
  }
</script>

querySelectorAll()

这是最灵活、最常用的获取多个元素的方法,它也使用 CSS 选择器,但会返回一个 静态的 NodeList,包含所有匹配的元素。

<ul>
  <li class="fruit">苹果</li>
  <li class="vegetable">胡萝卜</li>
  <li class="fruit">香蕉</li>
</ul>
<script>
  // 获取所有 class 为 "fruit" 的元素
  const fruits = document.querySelectorAll('.fruit');
  // 遍历并打印
  fruits.forEach(fruit => {
    console.log(fruit.textContent); // 输出 "苹果" 和 "香蕉"
  });
  // 获取所有的 <li> 标签
  const allListItems = document.querySelectorAll('li');
  console.log(allListItems.length); // 输出 3
</script>

获取元素的详细信息

找到元素之后,你通常还想获取它的具体信息。

获取文本内容

  • element.textContent: 获取元素及其所有后代的(不包含 HTML 标签)。
  • element.innerText: 获取元素渲染后的可见文本内容(会考虑 CSS 的 displayvisibility 属性)。
    • 推荐使用 textContent,因为它性能更好,并且不会受到 CSS 样式的影响。
<div id="info-box">你好,<strong>世界</strong>!</div>
<script>
  const infoBox = document.getElementById('info-box');
  console.log(infoBox.textContent); // 输出: "你好,世界!"
  console.log(infoBox.innerText);  // 输出: "你好,世界!"
</script>

获取和修改 HTML

  • element.innerHTML: 获取或设置元素内部的 HTML 代码使用时要小心,如果内容来自用户输入,可能会引发 XSS (跨站脚本) 攻击。
<div id="container"></div>
<script>
  const container = document.getElementById('container');
  // 设置 HTML
  container.innerHTML = '<p>这是一个新段落。</p><a href="#">一个链接</a>';
  // 获取 HTML
  console.log(container.innerHTML);
  // 输出: <p>这是一个新段落。</p><a href="#">一个链接</a>
</script>

获取和修改属性

  • element.getAttribute('attribute-name'): 获取指定属性的值。
  • element.setAttribute('attribute-name', 'new-value'): 设置指定属性的值。
  • element.hasAttribute('attribute-name'): 检查是否存在某个属性。
  • element.removeAttribute('attribute-name'): 移除某个属性。
  • element.property (快捷方式): 对于标准 HTML 属性(如 id, href, src, value),也可以直接通过 JS 属性访问。
<a id="my-link" href="https://old-site.com" target="_blank">旧链接</a>
<script>
  const link = document.getElementById('my-link');
  // 获取属性
  console.log(link.getAttribute('href')); // 输出: "https://old-site.com"
  console.log(link.href); // 输出: "https://old-site.com" (快捷方式)
  // 修改属性
  link.setAttribute('href', 'https://new-site.com');
  console.log(link.href); // 输出: "https://new-site.com"
  // 修改 data-* 属性 (自定义数据属性)
  link.setAttribute('data-info', '这是一个重要的链接');
  console.log(link.getAttribute('data-info')); // 输出: "这是一个重要的链接"
</script>

获取和修改样式

  • element.style.propertyName: 修改元素的内联样式(CSS 属性名需要转换为驼峰式,如 background-color -> backgroundColor)。
  • window.getComputedStyle(element): 获取元素最终计算出的所有样式(包括继承来的和通过 CSS 文件设置的)。
<p id="styled-text">这段文字会被修改样式。</p>
<script>
  const textElement = document.getElementById('styled-text');
  // 修改内联样式
  textElement.style.color = 'blue';
  textElement.style.fontSize = '20px';
  textElement.style.fontWeight = 'bold';
  // 获取计算后的样式
  const computedStyles = window.getComputedStyle(textElement);
  console.log(computedStyles.color); // 输出类似 "rgb(0, 0, 255)"
  console.log(computedStyles.fontSize); // 输出 "20px"
</script>

获取元素的层级关系

有时你需要根据元素在页面中的位置来找到它。

<div id="parent">
  <p id="child1">子元素 1</p>
  <p id="child2">子元素 2</p>
</div>
<script>
  const parent = document.getElementById('parent');
  const child1 = document.getElementById('child1');
  // 获取父元素
  console.log(child1.parentNode); // 或 child1.parentElement
  // 输出: <div id="parent">...</div>
  // 获取子元素列表 (HTMLCollection)
  console.log(parent.children);
  // 输出: [<p id="child1">...</p>, <p id="child2">...</p>]
  // 获取第一个/最后一个子元素
  console.log(parent.firstElementChild.textContent); // 输出: "子元素 1"
  console.log(parent.lastElementChild.textContent);  // 输出: "子元素 2"
  // 获取下一个/上一个兄弟元素
  console.log(child1.nextElementSibling.textContent); // 输出: "子元素 2"
  console.log(child2.previousElementSibling.textContent); // 输出: "子元素 1"
</script>

高级应用:从外部 API 获取信息并展示到网页

这通常被称为“获取数据”或“AJAX/Fetch”,这是一个非常常见的场景,即从服务器获取数据,然后用 JavaScript 将其动态地插入到网页中。

示例:从 JSONPlaceholder (一个免费的测试API) 获取待办事项列表并显示。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">获取外部信息</title>
  <style>
    body { font-family: sans-serif; }
    #todo-list { list-style: none; padding: 0; }
    #todo-list li { background: #f4f4f4; margin: 5px 0; padding: 10px; border-radius: 3px; }
  </style>
</head>
<body>
  <h1>我的待办事项</h1>
  <ul id="todo-list"></ul>
  <script>
    // 1. 找到要插入内容的容器
    const todoListElement = document.getElementById('todo-list');
    // 2. 使用 Fetch API 从服务器获取数据
    fetch('https://jsonplaceholder.typicode.com/todos')
      .then(response => {
        // 检查响应是否成功
        if (!response.ok) {
          throw new Error('网络响应不正常');
        }
        // 将响应体解析为 JSON
        return response.json();
      })
      .then(todos => {
        // 3. 遍历获取到的数据,并创建 HTML 元素插入到页面中
        todos.forEach(todo => {
          // 创建一个 <li> 元素
          const listItem = document.createElement('li');
          // 设置 <li> 的文本内容
          listItem.textContent = `${todo.id}: ${todo.title} (${todo.completed ? '已完成' : '未完成'})`;
          // 将 <li> 添加到 <ul> 中
          todoListElement.appendChild(listItem);
        });
      })
      .catch(error => {
        // 处理请求过程中可能发生的错误
        console.error('获取数据时出错:', error);
        todoListElement.textContent = '加载待办事项失败。';
      });
  </script>
</body>
</html>

总结与最佳实践

  1. 首选 querySelectorquerySelectorAll:它们功能强大,语法统一(使用 CSS 选择器),能满足绝大多数需求。
  2. 明确目标:如果你确定要找的元素有唯一的 idgetElementById 是最快的选择。
  3. 注意返回值类型getElementBy... 返回的是 HTMLCollection(动态的),而 querySelectorAll 返回的是 NodeList(静态的),了解它们的区别有助于避免在遍历后修改 DOM 时出现问题。
  4. 安全第一:使用 innerHTML 时,确保内容是可信的,以防止 XSS 攻击,对于动态内容,优先使用 textContentdocument.createElement + appendChild 的方式。
  5. 从实践中学习:尝试在浏览器的开发者工具(按 F12 打开)的 "Console" 中直接运行这些代码,并观察 "Elements" 面板的变化,这是最快的学习方式。