HOWTO · JavaScript

在 JavaScript 中按类获取子元素

本教程介绍了如何使用 JavaScript 在父元素中按类名获取一个或多个子元素,并说明如何选择直接子元素或嵌套的后代元素。

本页内容

要在父元素内选择具有指定 class 的元素,可用 querySelector() 获取第一个匹配项,或用 querySelectorAll() 获取所有匹配项。这两种搜索都会包含所有匹配的后代元素,包括嵌套元素。若只需要直接子元素,请使用 :scope > 选择器。下方示例还会展示 getElementsByClassName(),以及子元素属性与按 class 搜索的区别。

在 JavaScript 中通过 class 获取子元素

根据需要的结果选择 API:querySelector() 返回一个元素或 null;querySelectorAll() 返回静态 NodeList;getElementsByClassName() 返回实时 HTMLCollection。document.getElementById() 可用于查找父元素,而 firstElementChild、lastElementChild 和 children 可访问元素子节点,但不会按 class 筛选。除非选择器明确限定为直接子元素,否则本文讨论的父元素范围内搜索都可能匹配嵌套的后代元素。

在 JavaScript 中使用 querySelector() 按 class 名称获取子元素

在父元素上使用以点号开头的 CSS class 选择器。下方独立的 JavaScript 示例会检查每个对象的 classes 数组是否包含 item 标记,并据此筛选对象数组;它不会查询 DOM。在后续的浏览器示例中,读取属性前请先确认父元素和匹配结果都存在,因为任一查找都可能返回 null。

运行下面的 JavaScript 示例,可以快速查看如何在多个元素中匹配 class 标记:

本节中的代码以及显示的标签或输出仍为英文。

const elements = [
  { text: 'One', classes: ['item', 'featured'] },
  { text: 'Two', classes: ['item'] },
  { text: 'Note', classes: ['note'] }
];

const matches = elements.filter(element => element.classes.includes('item'));
console.log(matches.map(element => element.text).join(', '));
One, Two

这个独立的浏览器页面演示如何选择第一个匹配项、所有匹配项,如何使用元素子节点属性、按类名查找,以及如何选择直接子元素。这是一个浏览器示例;提供的运行时快照中没有执行过它。

<!doctype html>
<html lang="en">
<body>
  <ul id="parent">
    <li class="item featured">One</li>
    <li class="item">Two</li>
    <li class="item"><span class="item">Nested three</span></li>
  </ul>
  <script>
    const parent = document.getElementById('parent');
    const first = parent?.querySelector('.item');

    console.log(first?.textContent); // One

    const matches = parent?.querySelectorAll('.item') ?? [];
    const lastItem = matches.length ? matches[matches.length - 1] : null;
    console.log(lastItem?.textContent.trim() ?? 'No matching item'); // Nested three
    console.log([...matches].map(element => element.textContent.trim()));

    const firstElement = parent?.firstElementChild;
    const lastElement = parent?.lastElementChild;
    const childElements = parent?.children;
    console.log(firstElement?.textContent.trim(), lastElement?.textContent.trim());
    console.log(childElements?.length); // 3 direct element children

    const classMatches = parent?.getElementsByClassName('item');
    console.log(classMatches?.length); // 4 matching descendants

    const directMatches = parent?.querySelectorAll(':scope > .item') ?? [];
    console.log(directMatches.length); // 3 direct matches
  </script>
</body>
</html>

选择器 .item 会匹配带有该 class 标记的元素,无论它是否还具有其他 class。querySelector() 始终只返回文档顺序中第一个匹配的后代元素;它本身不能直接获取任意最后一个匹配项。在浏览器示例中,querySelectorAll('.item') 会收集所有匹配项,随后仅在列表非空时通过长度检查选择最后一项。根据所提供的标记,最后一个匹配项是包含“Nested three.”的嵌套 span。如果没有匹配项,受保护的表达式会让 lastItem 保持为 null,并记录 No matching item。如果要求两个 class 标记都存在,可以写成 .item.featured 这样的选择器,中间不要加空格。在 CSS 选择器中,空格表示后代关系,并不表示“任一 class”。

在 JavaScript 中使用 querySelectorAll() 方法获取所有具有相同 class 的子节点

调用 parent.querySelectorAll('.item') 获取所有匹配该 class 选择器的后代元素,然后遍历返回的 NodeList。示例中的嵌套 span 也具有 item,因此会包含在结果中。如果只要匹配直接子元素,请使用最后一节介绍的 :scope 方法。

querySelectorAll() 返回的 NodeList 是静态的:它记录调用时的匹配结果,不会随着之后的 DOM 变化而更新。再次调用该方法才能获取更新后的结果。选择器语法无效时会触发 SyntaxError DOMException;它不会返回 SYNTAX_ERR 之类的字符串。请确保选择器文本符合有效的 CSS 语法,尤其是在根据含标点符号的类名构造选择器时。

在 JavaScript 中将 document.getElementById() 与子节点属性搭配使用

document.getElementById('parent') 根据唯一的 id 查找父元素;它不会搜索具有指定 class 的子元素。找到父元素后,firstElementChild 和 lastElementChild 分别返回它的第一个和最后一个元素子节点。children 则提供其直接元素子节点的集合。这些属性不会按 class 筛选,并且会排除 HTML 标签之间的空白等文本节点。如果需要根据位置访问元素,或获取所有直接元素子节点,可以使用这些属性;如果要按 class 筛选,请使用选择器或类名方法。

在 JavaScript 中使用 getElementsByClassName() 方法按 class 名称获取一个或多个子元素

调用 parent.getElementsByClassName('item'),传入类名且不要加开头的点号。它会返回所有匹配的后代元素,包括嵌套元素。若要要求多个 class 标记同时存在,请用空格分隔它们,例如 getElementsByClassName('item featured');元素必须具有列出的每个类名。此 API 返回实时 HTMLCollection,因此其长度和成员会随匹配元素的 DOM 变化而更新。如果要在修改 DOM 时使用固定快照,请先用 Array.from(...) 复制该集合,再进行遍历。

在 JavaScript 中使用 :scope 获取直接子元素

在选择器前加上 :scope >,即可只匹配父元素的直接子元素。例如,parent.querySelectorAll(':scope > .item') 不会包含嵌套的 span.item,而 parent.querySelectorAll('.item') 会包含它。> 组合符表示直接子元素关系;没有它时,class 选择器也会搜索更深层的后代元素。当“子元素”特指紧邻父元素的元素,而不是父元素内部任意层级的元素时,这一区别是关键。