原生JS与jQuery获取兄弟元素的代码写法对比

作者:页面旅途

时间:2026-11-05

浏览:216

深入对比原生JavaScript与jQuery在获取兄弟元素时的API差异与底层实现,涵盖相邻节点、全部兄弟、区间遍历及选择器过滤的完整代码示例与避坑指南。

文章目录
核心结论先行:在 jQuery 中,获取兄弟元素封装为统一的集合方法(如 .siblings()、.next()、.prev()、.nextAll()、.prevAll()),返回类数组的 jQuery 对象且容忍空查询;而在原生现代 JavaScript 中,相邻元素已具备高效的只读属性 nextElementSibling 与 previousElementSibling,全量兄弟与区间兄弟则依托父级子元素列表 parentElement.children 配合标准数组方法(filter)或 while 循环实现精准抓取。
原生JS与jQuery获取兄弟元素的代码写法对比
图 0:原生 JS 与 jQuery 兄弟元素定位方式宏观对照概览

一、获取相邻兄弟元素:next() 与 prev() 的原生替代

在界面交互中最常见的场景是获取当前元素的“下一个元素”或“上一个元素”。例如在手风琴折叠菜单中点击标题展开紧随其后的内容容器,或在分步表单中高亮前一个步骤节点。

1. 获取下一个相邻元素

在 jQuery 中,通过 .next() 方法即可捕获紧随其后的同级元素:

// jQuery 写法
const $next = $('#current-item').next();
if ($next.length > 0) {
  $next.addClass('highlight');
}

而在原生 JavaScript 中,对应的现代标准属性是 nextElementSibling:

// 原生 JS 写法
const el = document.getElementById('current-item');
const nextEl = el.nextElementSibling;
if (nextEl) {
  nextEl.classList.add('highlight');
}

2. 获取上一个相邻元素

获取前驱相邻元素的对应关系如下:

// jQuery 写法
const $prev = $('#current-item').prev();

// 原生 JS 现代写法
const el = document.getElementById('current-item');
const prevEl = el.previousElementSibling;

3. 核心避坑:为什么必须用 ElementSibling 而非 Sibling?

初学者在编写原生 JavaScript 时,最常犯的错误是误调用 el.nextSibling 或 el.previousSibling。这两个早期的 DOM Level 1 属性返回的是 Node(节点) 而非仅针对 Element(元素)。

避坑重点:在 HTML 源码排版中,标签之间的回车换行、制表符和空格字符会被浏览器 DOM 树解析为独立的 TextNode(文本节点,nodeType === 3)。如果使用 nextSibling,获取到的往往是一段看不到的空白文本节点,直接访问 classList 或样式便会引发 TypeError: Cannot read properties of undefined。
原生JS与jQuery获取兄弟元素的代码写法对比
图 1:DOM 树中 Element 节点与 Text 节点的排列及查找边界

现代标准为此专门引入了带有 Element 标识的只读属性,它们在规范底层已经自动滤除了注释(Comment)和文本节点(Text),执行逻辑与 jQuery 保持一致。

二、获取全部兄弟元素:siblings() 的原生实现与步骤

jQuery 的 $(el).siblings() 能够一次性取得同级的所有其他元素,并自动将自身排除在返回结果之外。在原生 JS 中并没有单个内建方法名为 getSiblings(),但借助父子层级关系,实现步骤非常清晰规范。

1. 原生实现的具体操作步骤

  1. 寻找定位父容器:通过目标元素的 parentElement 向上查找直属父节点。如果目标元素尚未挂载到 DOM 树中或为根节点,则安全退出。
  2. 提取子元素集合:读取 parent.children。这里得到的是一个实时的 HTMLCollection,它天生只包含元素节点,自动规避了换行文本干扰。
  3. 剔除当前基准元素:将类数组集合通过 Array.from() 转换为数组实例,借助 filter 排除 child === el 的自身项。
  4. 按需执行选择器过滤:如果传入了 CSS 选择器参数,继续调用元素原生的 matches(selector) 进行二次筛选。
原生JS与jQuery获取兄弟元素的代码写法对比
图 2:原生 JS 模拟 siblings() 的算法执行流程与标准化步骤

2. 生产级原生 siblings 工具函数封装

为了在项目中随处复用,可以将上述步骤封装为一个高效、容错的纯工具函数:

/**
 * 获取指定元素的所有兄弟元素(原生实现)
 * @param {Element} el - 当前基准 DOM 元素
 * @param {string|null} selector - 可选的 CSS 选择器过滤条件
 * @returns {Element[]} 兄弟元素数组
 */
function getSiblings(el, selector = null) {
  if (!el || !el.parentElement) {
    return [];
  }

  // 提取父节点下所有子元素,转为数组并滤除自身
  const siblings = Array.from(el.parentElement.children).filter(child => child !== el);

  // 若无选择器限制,直接返回
  if (!selector) {
    return siblings;
  }

  // 使用标准 matches API 进行选择器过滤
  return siblings.filter(child => child.matches(selector));
}

// 使用示例对比:
// jQuery: $('#tab-btn').siblings('.active').removeClass('active');
// 原生写法:
const currentBtn = document.querySelector('#tab-btn');
getSiblings(currentBtn, '.active').forEach(sibling => {
  sibling.classList.remove('active');
});

三、获取区间兄弟元素:nextAll() 与 prevAll() 的原生写法

在数据表格高亮、问卷进度展示等场景中,经常需要选择“当前项之后的所有项”或“当前项之前的所有项”。jQuery 提供了 .nextAll() 与 .prevAll() 实现这种单向扫描。

1. 为什么原生没有 nextAll 属性?

W3C DOM 规范主要提供基础的图结构导航属性(父、子、前、后)。像区间扫描这类复合操作,原生鼓励开发者根据具体数据结构利用简单的循环(while loop)在常数空间内完成累加,避免在底层维护多套重叠的集合属性。

原生JS与jQuery获取兄弟元素的代码写法对比
图 3:prevAll 与 nextAll 遍历机制及原生 while 循环扫描示意

2. nextAll 与 prevAll 的原生函数实现

利用 nextElementSibling 与 previousElementSibling 作为迭代指针,构建单向遍历非常直观:

/**
 * 获取当前元素之后的所有同级兄弟元素
 */
function getNextSiblings(el, selector = null) {
  const results = [];
  let current = el ? el.nextElementSibling : null;

  while (current) {
    if (!selector || current.matches(selector)) {
      results.push(current);
    }
    current = current.nextElementSibling;
  }
  return results;
}

/**
 * 获取当前元素之前的所有同级兄弟元素
 * 注意:jQuery 的 prevAll 默认按从近到远的逆序排列,若需与之一致,直接 push 即可
 */
function getPrevSiblings(el, selector = null) {
  const results = [];
  let current = el ? el.previousElementSibling : null;

  while (current) {
    if (!selector || current.matches(selector)) {
      results.push(current);
    }
    current = current.previousElementSibling;
  }
  return results;
}
排列顺序小细节:jQuery 的 prevAll() 返回的元素顺序是从基准点向页面开头逆向推进的(即 index 0 是距离最近的前一个元素)。上述原生 while 循环收集方式与 jQuery 默认的遍历序完全一致。若需要按照在 DOM 树中从上到下的自然文档序,可对数组调用 results.reverse()。

四、返回值特征、错误容忍度与性能考量

除了书写方式上的不同,原生 JS 与 jQuery 在返回值类型、容错设计和运行时性能上也有着本质差异:

1. 集合包装 vs 纯净对象

  • jQuery 的包装特性:无论匹配到一个、多个还是零个元素,所有方法一律返回一个包装过的 jQuery 类数组对象。这种设计的好处是支持链式调用(如 $(el).next().addClass('x')),即使没有找到下一个元素,也不会抛出空指针异常,而是静默失败。
  • 原生 JS 的精准返回:单元素属性(如 nextElementSibling)在未匹配时返回 null;多元素操作返回原生 Array。原生返回的是裸 DOM 元素对象,具备极高的内存利用率,同时可以无缝对接 ES6+ 语法。

2. 空值保护与现代可选链(?.)

在原生 JS 中操作兄弟元素时,必须具备防御性编程意识。在现代 JavaScript(ES2020+)中,推荐使用可选链操作符(Optional Chaining)来实现类似 jQuery 的安全链式调用:

// 原生现代空值安全调用写法
const current = document.querySelector('.step-active');

// 如果没有下一个元素,?. 运算符直接返回 undefined,不会抛出 TypeError
current?.nextElementSibling?.classList.add('step-ready');
原生JS与jQuery获取兄弟元素的代码写法对比
图 4:原生 JS 与 jQuery 兄弟元素 API 综合速查矩阵

3. 性能考量与架构演进

随着现代浏览器对 DOM 规范的高质量实现,原生 API(如 parentElement、children、nextElementSibling)均由浏览器内核 C++ 原生提供,其查询性能显著超越经过 JavaScript 运行时封装的 jQuery 包装器。在大型长列表(如虚拟滚动、复杂表格)的渲染与交互中,使用纯原生兄弟元素遍历能够彻底消除 jQuery 包装对象的内存分配开销,大幅降低 GC(垃圾回收)压力。

五、全景对照与实用选择建议

功能需求 jQuery 语法 原生 JS 推荐写法 兼容性与注意事项
下一个兄弟元素 $(el).next() el.nextElementSibling 现代浏览器及 IE9+ 全面支持,无元素时返回 null
上一个兄弟元素 $(el).prev() el.previousElementSibling 现代浏览器及 IE9+ 全面支持,无元素时返回 null
所有同级兄弟 $(el).siblings() [...el.parentNode.children].filter(c => c !== el) 现代标准,返回纯原生数组,可直接使用 map/forEach
之后所有兄弟 $(el).nextAll() while 循环沿 nextElementSibling 累加 原生遍历性能极高,无多余封装层
之前所有兄弟 $(el).prevAll() while 循环沿 previousElementSibling 累加 注意数组元素顺序与文档流自然顺序的转换
带选择器筛选 $(el).siblings('.cls') 在过滤逻辑中加入 child.matches('.cls') matches() 支持任意合法 CSS 选择器表达式

总结而言,在维护已有 jQuery 老旧系统时,继续使用其兄弟查询方法能够保持代码风格统一;但在所有全新的前端模块、轻量级组件、无框架微前端或现代工程项目中,直接选用原生 ElementSibling 属性配合标准数组方法是更为轻量、高效且符合现代 Web 标准的最佳实践。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章