pin_drop当前位置:知识文库 ❯ 图文

JS集成:HTML DOM选择元素 - 详细教程与实战指南

教程简介

DOM元素选择是JavaScript操作网页的基础,所有DOM操作都始于选择目标元素。浏览器提供了多种选择元素的方法,从最早的getElementById到现代的querySelectorquerySelectorAll,每种方法都有其适用场景和性能特点。本教程将全面讲解所有DOM选择方法,包括它们的语法、用法、返回值类型、性能对比以及最佳实践。


核心概念

DOM选择方法的分类

DOM选择方法可以分为两大类:

  1. 传统方法getElementByIdgetElementsByClassNamegetElementsByTagNamegetElementsByName

  2. 现代方法querySelectorquerySelectorAllmatchesclosest

返回值类型

类型 描述 是否实时 支持forEach
Element 单个元素 - -
HTMLCollection 元素集合 是(实时)
NodeList 节点集合 静态/实时 部分(querySelectorAll返回的支持)

实时与静态的区别:

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>实时与静态集合的区别</title>
</head>
<body>
  <div class="item">项目1</div>
  <div class="item">项目2</div>
  <div class="item">项目3</div>

  <script>
    // HTMLCollection:实时的
    var liveCollection = document.getElementsByClassName('item');
    console.log('初始数量:', liveCollection.length); // 3

    // 添加新元素
    var newItem = document.createElement('div');
    newItem.className = 'item';
    newItem.textContent = '项目4';
    document.body.appendChild(newItem);

    console.log('添加后数量:', liveCollection.length); // 4(自动更新)

    // NodeList(querySelectorAll):静态的
    var staticList = document.querySelectorAll('.item');
    console.log('静态列表初始数量:', staticList.length); // 4

    // 再添加一个元素
    var newItem2 = document.createElement('div');
    newItem2.className = 'item';
    newItem2.textContent = '项目5';
    document.body.appendChild(newItem2);

    console.log('静态列表添加后数量:', staticList.length); // 4(不会更新)
    console.log('实时列表添加后数量:', liveCollection.length); // 5(自动更新)
  </script>
</body>
</html>

语法与用法

1. getElementById

getElementById 通过元素的ID属性选择单个元素,返回匹配的第一个元素。

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>getElementById示例</title>
</head>
<body>
  <div id="header">头部区域</div>
  <div id="content">内容区域</div>
  <div id="footer">底部区域</div>

  <script>
    // 基本用法
    var header = document.getElementById('header');
    console.log(header.textContent); // 头部区域

    // ID不存在时返回null
    var notFound = document.getElementById('nonexistent');
    console.log(notFound); // null

    // 注意:ID在HTML中应该唯一,如果有重复ID,只返回第一个
    // 大小写敏感
    var content = document.getElementById('Content'); // null(大小写不匹配)
    var content2 = document.getElementById('content'); // 正确

    // 修改元素
    header.style.color = 'blue';
    header.textContent = '修改后的头部';
  </script>
</body>
</html>

特点:

  • 只能在document上调用,不能在元素上调用

  • 返回单个Element对象或null

  • 性能最优的选择方法

  • ID必须唯一,重复ID行为未定义

2. getElementsByClassName

getElementsByClassName 通过类名选择元素,返回实时的HTMLCollection

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>getElementsByClassName示例</title>
</head>
<body>
  <div class="box primary">主要盒子</div>
  <div class="box secondary">次要盒子</div>
  <div class="box primary highlight">高亮主要盒子</div>
  <p class="box">段落盒子</p>

  <script>
    // 基本用法:选择所有匹配类名的元素
    var boxes = document.getElementsByClassName('box');
    console.log('box数量:', boxes.length); // 4

    // 多类名选择:同时匹配多个类名(空格分隔)
    var primaryBoxes = document.getElementsByClassName('box primary');
    console.log('box primary数量:', primaryBoxes.length); // 2

    // 在特定元素内搜索
    var container = document.getElementById('container');
    // var items = container.getElementsByClassName('item');

    // 遍历HTMLCollection
    for (var i = 0; i < boxes.length; i++) {
      console.log(boxes[i].textContent);
    }

    // 转换为数组(推荐)
    var boxesArray = Array.from(boxes);
    boxesArray.forEach(function(box) {
      console.log(box.textContent);
    });

    // 或者使用展开运算符
    var boxesArray2 = [...boxes];
  </script>
</body>
</html>

特点:

  • 可以在document和任意Element上调用

  • 返回实时的HTMLCollection

  • 支持多类名匹配(空格分隔,顺序无关)

  • 类名顺序不影响匹配结果

3. getElementsByTagName

getElementsByTagName 通过标签名选择元素,返回实时的HTMLCollection

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>getElementsByTagName示例</title>
</head>
<body>
  <h1>主标题</h1>
  <h2>副标题1</h2>
  <p>段落1</p>
  <h2>副标题2</h2>
  <p>段落2</p>

  <script>
    // 基本用法
    var paragraphs = document.getElementsByTagName('p');
    console.log('段落数量:', paragraphs.length); // 2

    // 获取所有元素
    var allElements = document.getElementsByTagName('*');
    console.log('所有元素数量:', allElements.length);

    // 在特定元素内搜索
    var container = document.body;
    var headings = container.getElementsByTagName('h2');
    console.log('h2数量:', headings.length); // 2

    // 遍历
    for (var i = 0; i < paragraphs.length; i++) {
      paragraphs[i].style.color = 'green';
    }

    // HTMLCollection的命名访问
    // 如果元素有name属性,可以通过命名访问
    // var form = document.getElementsByTagName('form')['myForm'];
  </script>
</body>
</html>

特点:

  • 可以在document和任意Element上调用

  • 返回实时的HTMLCollection

  • 标签名不区分大小写(HTML文档中)

  • * 表示选择所有元素

4. getElementsByName

getElementsByName 通过元素的name属性选择元素,返回实时的NodeList

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>getElementsByName示例</title>
</head>
<body>
  <form id="myForm">
    <input type="radio" name="gender" value="male"> 男
    <input type="radio" name="gender" value="female"> 女
    <input type="text" name="username" placeholder="用户名">
    <input type="email" name="email" placeholder="邮箱">
  </form>

  <script>
    // 获取所有name="gender"的元素
    var genders = document.getElementsByName('gender');
    console.log('gender元素数量:', genders.length); // 2

    // 获取选中的单选按钮的值
    for (var i = 0; i < genders.length; i++) {
      if (genders[i].checked) {
        console.log('选中的性别:', genders[i].value);
      }
    }

    // 注意:只能在document上调用
    // document.getElementsByName('username')
  </script>
</body>
</html>

特点:

  • 只能在document上调用

  • 返回实时的NodeList

  • 主要用于表单元素

5. querySelector

querySelector 使用CSS选择器语法选择第一个匹配的元素。

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>querySelector示例</title>
</head>
<body>
  <div id="container">
    <h2 class="title">标题</h2>
    <ul class="list">
      <li class="item active">项目1</li>
      <li class="item">项目2</li>
      <li class="item">项目3</li>
    </ul>
    <p data-role="description">描述文本</p>
  </div>

  <script>
    // 基本选择器
    var container = document.querySelector('#container');
    var title = document.querySelector('.title');
    var firstItem = document.querySelector('li');
    var desc = document.querySelector('[data-role="description"]');

    // 组合选择器
    var activeItem = document.querySelector('li.active');
    var listTitle = document.querySelector('#container > .title');

    // 伪类选择器
    var firstChild = document.querySelector('li:first-child');
    var lastChild = document.querySelector('li:last-child');
    var notActive = document.querySelector('li:not(.active)');

    // 在特定元素内搜索
    var context = document.querySelector('#container');
    var itemInContext = context.querySelector('.item');

    console.log('active项目:', activeItem.textContent);
    console.log('描述:', desc.textContent);
  </script>
</body>
</html>

特点:

  • 可以在document和任意Element上调用

  • 返回第一个匹配的Elementnull

  • 支持完整的CSS选择器语法

  • 返回静态结果

6. querySelectorAll

querySelectorAll 使用CSS选择器语法选择所有匹配的元素,返回静态的NodeList

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>querySelectorAll示例</title>
</head>
<body>
  <div id="app">
    <nav>
      <a href="#home" class="nav-link active">首页</a>
      <a href="#about" class="nav-link">关于</a>
      <a href="#contact" class="nav-link">联系</a>
    </nav>
    <main>
      <article class="post">
        <h3>文章1</h3>
        <p class="excerpt">摘要1</p>
      </article>
      <article class="post">
        <h3>文章2</h3>
        <p class="excerpt">摘要2</p>
      </article>
    </main>
  </div>

  <script>
    // 基本用法
    var navLinks = document.querySelectorAll('.nav-link');
    console.log('导航链接数量:', navLinks.length); // 3

    // 复杂选择器
    var postTitles = document.querySelectorAll('.post > h3');
    var activeLinks = document.querySelectorAll('.nav-link.active');
    var allLinksAndExcerpts = document.querySelectorAll('a, .excerpt');

    // 属性选择器
    var links = document.querySelectorAll('a[href^="#"]');

    // 伪类选择器
    var firstPost = document.querySelectorAll('.post:first-of-type');
    var oddItems = document.querySelectorAll('.post:nth-child(odd)');

    // 遍历NodeList
    navLinks.forEach(function(link) {
      console.log(link.textContent, link.href);
    });

    // 转换为数组
    var linksArray = Array.from(navLinks);

    // 在特定元素内搜索
    var app = document.querySelector('#app');
    var postsInApp = app.querySelectorAll('.post');
  </script>
</body>
</html>

特点:

  • 可以在document和任意Element上调用

  • 返回静态的NodeList

  • 支持完整的CSS选择器语法

  • 返回的NodeList支持forEach(现代浏览器)

7. matches

matches 方法检测元素是否匹配指定的CSS选择器。

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>matches示例</title>
</head>
<body>
  <div id="container">
    <button class="btn btn-primary" data-action="save">保存</button>
    <button class="btn btn-danger" data-action="delete">删除</button>
    <a href="#" class="btn btn-secondary">链接按钮</a>
  </div>

  <script>
    var primaryBtn = document.querySelector('.btn-primary');

    // 检测是否匹配选择器
    console.log(primaryBtn.matches('.btn'));           // true
    console.log(primaryBtn.matches('.btn-primary'));   // true
    console.log(primaryBtn.matches('.btn-danger'));    // false
    console.log(primaryBtn.matches('button'));         // true
    console.log(primaryBtn.matches('[data-action]'));  // true

    // 实际应用:事件委托中过滤目标元素
    document.getElementById('container').addEventListener('click', function(e) {
      if (e.target.matches('.btn')) {
        console.log('点击了按钮:', e.target.dataset.action);
      }
    });

    // 兼容性写法
    function matches(element, selector) {
      return element.matches ||
             element.msMatchesSelector ||
             element.webkitMatchesSelector;
    }
    // if (matches(element, '.active')) { ... }
  </script>
</body>
</html>

8. closest

closest 方法从当前元素开始,向上遍历DOM树,返回匹配指定选择器的最近祖先元素(包括自身)。

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>closest示例</title>
</head>
<body>
  <div class="card" data-id="1">
    <div class="card-header">
      <h3 class="card-title">卡片标题</h3>
    </div>
    <div class="card-body">
      <p class="card-text">卡片内容</p>
      <button class="btn btn-delete" data-action="delete">删除</button>
    </div>
  </div>

  <script>
    var deleteBtn = document.querySelector('.btn-delete');

    // 查找最近的匹配祖先
    var card = deleteBtn.closest('.card');
    console.log('卡片ID:', card.dataset.id); // 1

    var cardBody = deleteBtn.closest('.card-body');
    console.log('卡片body:', cardBody.className);

    // 自身也匹配时返回自身
    var btn = deleteBtn.closest('.btn');
    console.log('自身匹配:', btn === deleteBtn); // true

    // 没有匹配时返回null
    var nav = deleteBtn.closest('nav');
    console.log('无匹配:', nav); // null

    // 实际应用:事件委托中查找目标容器
    document.addEventListener('click', function(e) {
      var card = e.target.closest('.card');
      if (card) {
        var action = e.target.closest('[data-action]');
        if (action) {
          console.log('操作:', action.dataset.action, '卡片:', card.dataset.id);
        }
      }
    });
  </script>
</body>
</html>

代码示例

示例1:综合选择器演示

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>综合选择器演示</title>
  <style>
    body { font-family: -apple-system, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
    .demo-area { border: 1px solid #ddd; border-radius: 8px; padding: 20px; margin: 20px 0; }
    .highlight { background: #fff3cd; outline: 2px solid #ffc107; }
    .result { background: #f5f5f5; padding: 10px; border-radius: 4px; margin: 8px 0; font-family: monospace; }
    button { padding: 6px 12px; margin: 4px; border: 1px solid #ccc; border-radius: 4px; cursor: pointer; }
    button:hover { background: #e9e9e9; }
  </style>
</head>
<body>
  <h1>综合选择器演示</h1>

  <div class="demo-area" id="demo">
    <h2 class="title" data-level="1">演示区域</h2>
    <p class="description">这是一个选择器演示区域</p>

    <ul class="list" id="item-list">
      <li class="item active" data-index="0">项目 1(激活)</li>
      <li class="item" data-index="1">项目 2</li>
      <li class="item" data-index="2">项目 3</li>
      <li class="item special" data-index="3">项目 4(特殊)</li>
      <li class="item" data-index="4">项目 5</li>
    </ul>

    <div class="actions">
      <button class="btn" data-action="select">选择</button>
      <button class="btn btn-primary" data-action="highlight">高亮</button>
      <button class="btn btn-danger" data-action="reset">重置</button>
    </div>
  </div>

  <h2>选择器测试</h2>
  <div>
    <button onclick="testSelector('getElementById')">getElementById</button>
    <button onclick="testSelector('getElementsByClassName')">getElementsByClassName</button>
    <button onclick="testSelector('getElementsByTagName')">getElementsByTagName</button>
    <button onclick="testSelector('querySelector')">querySelector</button>
    <button onclick="testSelector('querySelectorAll')">querySelectorAll</button>
    <button onclick="testSelector('matches')">matches</button>
    <button onclick="testSelector('closest')">closest</button>
  </div>

  <div id="result" class="result">点击上方按钮查看选择器结果</div>

  <script>
    var resultEl = document.getElementById('result');

    function showResult(title, content) {
      resultEl.textContent = title + ':\n' + content;
    }

    function clearHighlights() {
      document.querySelectorAll('.highlight').forEach(function(el) {
        el.classList.remove('highlight');
      });
    }

    function highlightElements(elements) {
      clearHighlights();
      if (elements.length !== undefined) {
        for (var i = 0; i < elements.length; i++) {
          elements[i].classList.add('highlight');
        }
      } else if (elements) {
        elements.classList.add('highlight');
      }
    }

    function testSelector(method) {
      clearHighlights();
      var result = '';

      switch (method) {
        case 'getElementById':
          var el = document.getElementById('item-list');
          highlightElements(el);
          result = '找到元素:' + el.tagName + '#' + el.id;
          break;

        case 'getElementsByClassName':
          var els = document.getElementsByClassName('item');
          highlightElements(els);
          result = '找到 ' + els.length + ' 个元素(实时HTMLCollection)';
          break;

        case 'getElementsByTagName':
          var els2 = document.getElementsByTagName('li');
          highlightElements(els2);
          result = '找到 ' + els2.length + ' 个li元素';
          break;

        case 'querySelector':
          var el2 = document.querySelector('.item.active');
          highlightElements(el2);
          result = '找到第一个 .item.active:' + el2.textContent;
          break;

        case 'querySelectorAll':
          var els3 = document.querySelectorAll('.item:not(.active)');
          highlightElements(els3);
          result = '找到 ' + els3.length + ' 个非active的item(静态NodeList)';
          break;

        case 'matches':
          var firstItem = document.querySelector('.item');
          result = '第一个item匹配 .item: ' + firstItem.matches('.item') + '\n';
          result += '第一个item匹配 .active: ' + firstItem.matches('.active');
          break;

        case 'closest':
          var specialItem = document.querySelector('.special');
          var card = specialItem.closest('.demo-area');
          result = '.special 的最近 .demo-area 祖先:' + card.id;
          break;
      }

      showResult(method, result);
    }
  </script>
</body>
</html>

示例2:事件委托中的选择器应用

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>事件委托中的选择器应用</title>
  <style>
    body { font-family: -apple-system, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; }
    .todo-list { list-style: none; padding: 0; }
    .todo-item { display: flex; align-items: center; padding: 12px; border: 1px solid #eee; margin: 4px 0; border-radius: 6px; }
    .todo-item.completed { opacity: 0.6; text-decoration: line-through; }
    .todo-item .text { flex: 1; }
    .btn { padding: 4px 10px; border: none; border-radius: 4px; cursor: pointer; margin-left: 4px; }
    .btn-toggle { background: #4CAF50; color: white; }
    .btn-delete { background: #f44336; color: white; }
    .add-form { display: flex; gap: 8px; margin: 16px 0; }
    .add-form input { flex: 1; padding: 8px; border: 1px solid #ddd; border-radius: 4px; }
    .add-form button { padding: 8px 16px; background: #2196F3; color: white; border: none; border-radius: 4px; cursor: pointer; }
  </style>
</head>
<body>
  <h1>待办事项列表</h1>

  <div class="add-form">
    <input type="text" id="new-todo" placeholder="输入新的待办事项">
    <button id="add-btn">添加</button>
  </div>

  <ul class="todo-list" id="todo-list">
    <li class="todo-item" data-id="1">
      <span class="text">学习DOM选择器</span>
      <button class="btn btn-toggle" data-action="toggle">完成</button>
      <button class="btn btn-delete" data-action="delete">删除</button>
    </li>
    <li class="todo-item completed" data-id="2">
      <span class="text">学习HTML基础</span>
      <button class="btn btn-toggle" data-action="toggle">完成</button>
      <button class="btn btn-delete" data-action="delete">删除</button>
    </li>
    <li class="todo-item" data-id="3">
      <span class="text">学习CSS布局</span>
      <button class="btn btn-toggle" data-action="toggle">完成</button>
      <button class="btn btn-delete" data-action="delete">删除</button>
    </li>
  </ul>

  <script>
    var todoList = document.getElementById('todo-list');
    var newTodoInput = document.getElementById('new-todo');
    var addBtn = document.getElementById('add-btn');
    var nextId = 4;

    // 事件委托:在父元素上监听所有子元素的事件
    todoList.addEventListener('click', function(e) {
      // 使用closest查找目标todo项
      var todoItem = e.target.closest('.todo-item');
      if (!todoItem) return;

      // 使用matches判断点击的按钮类型
      if (e.target.matches('[data-action="toggle"]')) {
        todoItem.classList.toggle('completed');
        e.target.textContent = todoItem.classList.contains('completed') ? '撤销' : '完成';
      }

      if (e.target.matches('[data-action="delete"]')) {
        todoItem.remove();
      }
    });

    // 添加新待办事项
    addBtn.addEventListener('click', function() {
      var text = newTodoInput.value.trim();
      if (!text) return;

      var li = document.createElement('li');
      li.className = 'todo-item';
      li.dataset.id = nextId++;
      li.innerHTML =
        '<span class="text">' + text + '</span>' +
        '<button class="btn btn-toggle" data-action="toggle">完成</button>' +
        '<button class="btn btn-delete" data-action="delete">删除</button>';

      todoList.appendChild(li);
      newTodoInput.value = '';
    });
  </script>
</body>
</html>

示例3:选择器性能对比

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>选择器性能对比</title>
  <style>
    body { font-family: -apple-system, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
    .perf-result { margin: 10px 0; padding: 10px; background: #f5f5f5; border-radius: 4px; }
    .perf-bar { height: 20px; background: #2196F3; border-radius: 3px; transition: width 0.3s; }
    table { width: 100%; border-collapse: collapse; margin: 10px 0; }
    th, td { padding: 8px; text-align: left; border-bottom: 1px solid #ddd; }
    th { background: #f0f0f0; }
    button { padding: 10px 20px; background: #2196F3; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; }
    button:hover { background: #1976D2; }
  </style>
</head>
<body>
  <h1>选择器性能对比</h1>
  <p>生成1000个元素,对比不同选择方法的性能</p>
  <button id="run-test">运行性能测试</button>

  <div id="test-container" style="display:none;"></div>

  <table id="results-table">
    <thead>
      <tr>
        <th>选择方法</th>
        <th>执行次数</th>
        <th>总耗时(ms)</th>
        <th>平均耗时(ms)</th>
      </tr>
    </thead>
    <tbody id="results-body"></tbody>
  </table>

  <script>
    // 生成测试元素
    var container = document.getElementById('test-container');
    for (var i = 0; i < 1000; i++) {
      var div = document.createElement('div');
      div.className = 'test-item' + (i % 2 === 0 ? ' even' : ' odd');
      div.id = 'item-' + i;
      div.dataset.index = i;
      div.textContent = '测试项目 ' + i;
      container.appendChild(div);
    }

    document.getElementById('run-test').addEventListener('click', function() {
      var iterations = 1000;
      var results = [];

      // 测试 getElementById
      var start = performance.now();
      for (var i = 0; i < iterations; i++) {
        document.getElementById('item-500');
      }
      results.push({
        method: 'getElementById',
        total: (performance.now() - start).toFixed(3)
      });

      // 测试 getElementsByClassName
      start = performance.now();
      for (var i = 0; i < iterations; i++) {
        document.getElementsByClassName('test-item');
      }
      results.push({
        method: 'getElementsByClassName',
        total: (performance.now() - start).toFixed(3)
      });

      // 测试 getElementsByTagName
      start = performance.now();
      for (var i = 0; i < iterations; i++) {
        document.getElementsByTagName('div');
      }
      results.push({
        method: 'getElementsByTagName',
        total: (performance.now() - start).toFixed(3)
      });

      // 测试 querySelector (ID)
      start = performance.now();
      for (var i = 0; i < iterations; i++) {
        document.querySelector('#item-500');
      }
      results.push({
        method: 'querySelector(#id)',
        total: (performance.now() - start).toFixed(3)
      });

      // 测试 querySelector (class)
      start = performance.now();
      for (var i = 0; i < iterations; i++) {
        document.querySelector('.test-item');
      }
      results.push({
        method: 'querySelector(.class)',
        total: (performance.now() - start).toFixed(3)
      });

      // 测试 querySelectorAll (class)
      start = performance.now();
      for (var i = 0; i < iterations; i++) {
        document.querySelectorAll('.test-item');
      }
      results.push({
        method: 'querySelectorAll(.class)',
        total: (performance.now() - start).toFixed(3)
      });

      // 渲染结果
      var tbody = document.getElementById('results-body');
      tbody.innerHTML = '';
      results.forEach(function(r) {
        var avg = (parseFloat(r.total) / iterations).toFixed(6);
        var tr = document.createElement('tr');
        tr.innerHTML = '<td>' + r.method + '</td><td>' + iterations +
          '</td><td>' + r.total + '</td><td>' + avg + '</td>';
        tbody.appendChild(tr);
      });
    });
  </script>
</body>
</html>

选择器性能对比

性能排名(从快到慢)

排名 方法 性能 适用场景
1 getElementById 最快 通过唯一ID选择单个元素
2 getElementsByClassName 很快 通过类名选择多个元素
3 getElementsByTagName 很快 通过标签名选择多个元素
4 querySelector (ID选择器) 较快 使用CSS选择器选择单个元素
5 querySelector (类选择器) 中等 使用CSS选择器选择单个元素
6 querySelectorAll 较慢 使用CSS选择器选择多个元素

性能差异的原因

  1. getElementById:浏览器内部维护了ID到元素的哈希表,查找时间接近O(1)

  2. getElementsByClassName/TagName:浏览器对类名和标签名建立了索引,查找效率高

  3. querySelector/querySelectorAll:需要解析CSS选择器字符串,然后遍历DOM树匹配

选择建议

代码示例

// 规则1:能用getElementById就用getElementById
var element = document.getElementById('myId');  // 最快

// 规则2:需要多个元素时用getElementsByClassName或getElementsByTagName
var items = document.getElementsByClassName('item');  // 快

// 规则3:复杂选择器才用querySelector/querySelectorAll
var target = document.querySelector('.container > .item:nth-child(2)');  // 灵活

// 规则4:避免在循环中使用querySelectorAll
// 不推荐
for (var i = 0; i < 100; i++) {
  document.querySelectorAll('.item');  // 每次都重新查询
}

// 推荐
var items = document.querySelectorAll('.item');  // 缓存结果
for (var i = 0; i < items.length; i++) {
  // 使用items[i]
}

浏览器兼容性

选择方法兼容性

方法 Chrome Firefox Safari Edge IE
getElementById 全部 全部 全部 全部 6+
getElementsByClassName 全部 全部 全部 全部 9+
getElementsByTagName 全部 全部 全部 全部 6+
getElementsByName 全部 全部 全部 全部 6+
querySelector 1+ 3.5+ 3.1+ 12+ 8+
querySelectorAll 1+ 3.5+ 3.1+ 12+ 8+
matches 34+ 44+ 7+ 12+ 9+(msMatchesSelector)
closest 41+ 40+ 6+ 15+ 不支持
NodeList.forEach 51+ 50+ 10+ 16+ 不支持

closest和matches的兼容性写法

代码示例

// matches兼容
if (!Element.prototype.matches) {
  Element.prototype.matches =
    Element.prototype.msMatchesSelector ||
    Element.prototype.webkitMatchesSelector;
}

// closest兼容
if (!Element.prototype.closest) {
  Element.prototype.closest = function(selector) {
    var el = this;
    while (el) {
      if (el.matches(selector)) return el;
      el = el.parentElement;
    }
    return null;
  };
}

注意事项与最佳实践

1. 缓存选择结果

代码示例

// 不推荐:重复选择
function updateUI() {
  document.getElementById('header').style.color = 'red';
  document.getElementById('header').style.fontSize = '20px';
  document.getElementById('header').textContent = '新标题';
}

// 推荐:缓存选择结果
function updateUI() {
  var header = document.getElementById('header');
  header.style.color = 'red';
  header.style.fontSize = '20px';
  header.textContent = '新标题';
}

2. 限定搜索范围

代码示例

// 不推荐:在整个文档中搜索
var items = document.querySelectorAll('.item');

// 推荐:在特定容器内搜索
var container = document.getElementById('myContainer');
var items = container.querySelectorAll('.item');

3. 注意实时集合的陷阱

代码示例

// 陷阱:遍历实时集合时修改DOM
var items = document.getElementsByClassName('item');
for (var i = 0; i < items.length; i++) {
  // 如果删除元素,items.length会变化,导致跳过元素
  items[i].remove();
}

// 方案1:从后向前遍历
for (var i = items.length - 1; i >= 0; i--) {
  items[i].remove();
}

// 方案2:转为静态数组
var itemsArray = Array.from(document.getElementsByClassName('item'));
itemsArray.forEach(function(item) { item.remove(); });

// 方案3:使用querySelectorAll(静态集合)
var staticItems = document.querySelectorAll('.item');
staticItems.forEach(function(item) { item.remove(); });

4. 使用事件委托减少选择操作

代码示例

// 不推荐:为每个元素绑定事件
document.querySelectorAll('.btn').forEach(function(btn) {
  btn.addEventListener('click', handleClick);
});

// 推荐:使用事件委托
document.addEventListener('click', function(e) {
  if (e.target.matches('.btn')) {
    handleClick(e);
  }
});

代码规范示例

选择器使用规范

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>选择器使用规范</title>
</head>
<body>
  <div id="app">
    <nav class="nav" role="navigation">
      <a href="#home" class="nav-link">首页</a>
      <a href="#about" class="nav-link">关于</a>
    </nav>
    <main class="content" id="main-content">
      <section class="section" data-section="intro">
        <h2>介绍</h2>
      </section>
    </main>
  </div>

  <script>
    (function() {
      'use strict';

      // 1. 缓存常用DOM引用
      var app = document.getElementById('app');
      var mainContent = document.getElementById('main-content');
      var nav = document.querySelector('.nav');

      // 2. 限定搜索范围
      function getNavLinks() {
        return nav.querySelectorAll('.nav-link');
      }

      // 3. 使用matches进行条件判断
      function isNavLink(element) {
        return element.matches('.nav-link');
      }

      // 4. 使用closest查找容器
      function getSection(element) {
        return element.closest('.section');
      }

      // 5. 事件委托
      app.addEventListener('click', function(e) {
        var link = e.target.closest('.nav-link');
        if (link) {
          e.preventDefault();
          console.log('导航到:', link.getAttribute('href'));
        }
      });

      // 6. 批量操作使用静态集合
      function highlightAllSections() {
        var sections = mainContent.querySelectorAll('.section');
        sections.forEach(function(section) {
          section.classList.add('highlight');
        });
      }
    })();
  </script>
</body>
</html>

常见问题与解决方案

问题1:querySelector返回null

症状: document.querySelector('.my-class') 返回 null

解决方案:

代码示例

// 原因1:元素还未渲染(脚本在元素之前执行)
// 解决:使用DOMContentLoaded或defer
document.addEventListener('DOMContentLoaded', function() {
  var el = document.querySelector('.my-class');
});

// 原因2:选择器拼写错误
// 解决:在DevTools中测试选择器

// 原因3:元素在shadow DOM中
// 解决:先获取shadow root再查询
var shadowRoot = element.shadowRoot;
var innerEl = shadowRoot.querySelector('.my-class');

// 安全访问
var el = document.querySelector('.my-class');
if (el) {
  el.textContent = 'Hello';
} else {
  console.warn('元素未找到');
}

问题2:HTMLCollection不支持forEach

症状: document.getElementsByClassName('item').forEach is not a function

解决方案:

代码示例

// 方案1:转为数组
Array.from(document.getElementsByClassName('item')).forEach(function(item) {
  console.log(item);
});

// 方案2:使用展开运算符
[...document.getElementsByClassName('item')].forEach(function(item) {
  console.log(item);
});

// 方案3:使用for循环
var items = document.getElementsByClassName('item');
for (var i = 0; i < items.length; i++) {
  console.log(items[i]);
}

// 方案4:使用querySelectorAll(返回支持forEach的NodeList)
document.querySelectorAll('.item').forEach(function(item) {
  console.log(item);
});

问题3:选择器在iframe中无法找到元素

解决方案:

代码示例

// 在iframe中查找元素
var iframe = document.getElementById('myIframe');
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
var element = iframeDoc.querySelector('.target');

// 跨域iframe无法访问contentDocument

总结

DOM元素选择是所有DOM操作的起点,选择合适的方法能够提升代码质量和性能:

  1. getElementById:最快的选择方法,适合通过唯一ID选择元素

  2. getElementsByClassName/TagName:快速选择多个元素,返回实时集合

  3. querySelector/querySelectorAll:最灵活,支持完整CSS选择器语法

  4. matches:检测元素是否匹配选择器,常用于事件委托

  5. closest:向上查找最近的匹配祖先,事件委托的利器

  6. 性能优先级:getElementById > getElementsBy > querySelector

  7. 最佳实践:缓存选择结果、限定搜索范围、注意实时集合陷阱、使用事件委托

根据具体场景选择最合适的方法,在性能和灵活性之间取得平衡。

常见问题

问题1:querySelector返回null?

症状: document.querySelector('.my-class') 返回 null解决方案:

问题2:HTMLCollection不支持forEach?

症状: document.getElementsByClassName('item').forEach is not a function解决方案:

问题3:选择器在iframe中无法找到元素?

解决方案:

标签: Script标签 JavaScript DOM HTML CSS 选择器 节点操作

本文由小确幸生活整理发布,转载请注明出处

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: JS集成:HTML DOM概述 - 详细教程与实战指南 下一篇: JS集成:HTML DOM修改属性 - 详细教程与实战指南

poll相关推荐