ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Atom 仓库中的 jQuery 去依赖指南:基于 You-Dont-Need-jQuery 夹具的原生 DOM/BOM 替代实践

2026/9/5 16:10:18 拓冰建站 浏览量
Atom 仓库中的 jQuery 去依赖指南:基于 You-Dont-Need-jQuery 夹具的原生 DOM/BOM 替代实践 Atom 仓库中的 jQuery 去依赖指南:基于 You-Dont-Need-jQuery 夹具的原生 DOM/BOM 替代实践【免费下载链接】atom:atom: The hackable text editor项目地址: https://gitcode.com/gh_mirrors/at/atom本文以 Atom 仓库中spec/fixtures/git/repo-with-submodules/You-Dont-Need-jQuery/目录下的印尼语参考文档 README-id.md 为核心骨架,系统讲解如何用浏览器原生的 DOM/BOM API 逐项替代 jQuery 的常见用法,覆盖查询选择器、样式、DOM 操作、Ajax、事件与工具函数六大类;同时结合 Atom 仓库中引用该夹具的测试源码,说明这份文档在仓库内作为 git submodule 测试数据的角色,读完可掌握一套可直接复制、并在浏览器中落地的 jQuery 迁移方案。一、文档定位:Atom 仓库中的一份 jQuery 替代参考该文档是知名开源项目 You-Dont-Need-jQuery(由 OneUI Group 维护,MIT 许可)的印尼语版本 README,其核心观点是:随着前端环境快速演进,主流浏览器已经很好地实现了 DOM/BOM API,不再需要从 jQuery 学起才能做 DOM 操作与事件处理;在 React、Angular、Vue 等框架普及之后,直接操作 DOM 本身也逐渐成为一种需要谨慎对待的反模式——jQuery 因此越来越不必要。该文档给出了针对 IE 10 的 jQuery 原生替代实现。在 Atom 仓库中,这份文档所在的位置本身就有一个工程上的看点:spec/fixtures/git/repo-with-submodules/是一个带 submodule 的 git 仓库测试夹具。从源码结构看:.gitmodules 声明了两个 submodule:jstips与You-Dont-Need-jQuery;git.git/config 中同样记录了[submodule jstips]和[submodule You-Dont-Need-jQuery]两组配置;仓库的提交历史(git.git 的日志)显示,最后一次提交信息即为 submodules。Atom 的测试 spec/project-spec.js 在.observeRepositories()用例中,正是把repo-with-submodules/git.git复制到临时目录的.git下,验证atom.project能正确观察到一个含 submodule 的仓库,并断言observed[1].getReferenceTarget(refs/heads/master)等于d2b0ad9cbc6f6c4372e8956e5cc5af771b2342e5。也就是说,本文主角 README 所在的目录,首先服务于Atom 能否正确识别含 submodule 的 Git 仓库这一测试目标;其次,它本身就是一份内容完整的 jQuery 原生替代指南,下文即按其目录结构完整展开:Query Selector、CSS Style、DOM Manipulation、Ajax、Events、Utilities,外加 Translation 与 Browser Support 两个附录。二、Query Selector:从 jQuery 选择器到原生查询 API对于 class、id、属性这类常用选择器,可直接使用document.querySelector或document.querySelectorAll作为替代。两者区别:document.querySelector返回第一个匹配的元素;document.querySelectorAll返回所有匹配元素,组成 NodeList;需要数组时可用[].slice.call(document.querySelectorAll(selector) || [])转换;若无匹配结果:jQuery 返回[],而 DOM API 返回null——需要警惕空指针问题,可以用||提供初始值:document.querySelectorAll(selector) || []。注意(原文档强调):querySelector/querySelectorAll相对偏慢。追求性能时可改用getElementById、document.getElementsByClassName或document.getElementsByTagName。2.1 基础查询(1.0 – 1.3)// 1.0 Query by selector // jQuery $(selector); // Native document.querySelectorAll(selector); // 1.1 Query by class // jQuery $(.class); // Native document.querySelectorAll(.class); // or document.getElementsByClassName(class); // 1.2 Query by id // jQuery $(#id); // Native document.querySelector(#id); // or document.getElementById(id); // 1.3 Query using attribute // jQuery $(a[target_blank]); // Native document.querySelectorAll(a[target_blank]);2.2 后代查找、属性与 data 属性(1.4)// 查找子节点 // jQuery $el.find(li); // Native el.querySelectorAll(li); // 查找 body // jQuery $(body); // Native document.body; // 获取属性 // jQuery $el.attr(foo); // Native el.getAttribute(foo); // 获取 data 属性 // jQuery $el.data(foo); // Native(用 getAttribute) el.getAttribute(data-foo); // 若需要兼容到 IE 11,还可以使用 dataset el.dataset[foo];2.3 Sibling / Previous / Next 兄弟元素(1.5)// 兄弟元素(排除自身) // jQuery $el.siblings(); // Native [].filter.call(el.parentNode.children, function(child) { return child ! el; }); // 前一个元素 // jQuery $el.prev(); // Native el.previousElementSibling; // 后一个元素 // jQuery $el.next(); // Native el.nextElementSibling;2.4 closest:向祖先方向查找第一个匹配元素(1.6)closest返回从当前元素出发、直到 document 为止的第一个匹配 selector 的元素。原生实现如下:// jQuery $el.closest(queryString); // Native function closest(el, selector) { const matchesSelector el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; while (el) { if (matchesSelector.call(el, selector)) { return el; } else { el el.parentElement; } } return null; }这里通过matches / webkitMatchesSelector / mozMatchesSelector / msMatchesSelector做多引擎前缀兼容,正是面向 IE 10 的写法。2.5 parentsUntil:获取限定范围内的祖先链(1.7)parentsUntil获取每个匹配元素的 ancestor(不包含 selector 搜索得到的当前元素本身)。原生实现:// jQuery $el.parentsUntil(selector, filter); // Native function parentsUntil(el, selector, filter) { const result []; const matchesSelector el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; // 从父元素开始匹配 el el.parentElement; while (el !matchesSelector.call(el, selector)) { if (!filter) { result.push(el); } else { if (matchesSelector.call(el, filter)) { result.push(el); } } el el.parentElement; } return result; }2.6 表单取值与索引(1.8)// 输入框/文本域取值 // jQuery $(#my-input).val(); // Native document.querySelector(#my-input).value; // 获取 e.currentTarget 在 .radio 集合中的下标 // jQuery $(e.currentTarget).index(.radio); // Native [].indexOf.call(document.querySelectorAll(.radio), e.currentTarget);2.7 Iframe 内容访问(1.9)$(iframe).contents()对应原生的contentDocument:// Iframe 文档 // jQuery $iframe.contents(); // Native iframe.contentDocument; // 在 Iframe 内查询 // jQuery $iframe.contents().find(.css); // Native iframe.contentDocument.querySelectorAll(.css);三、CSS Style:计算样式、类名与尺寸3.1 读取与设置样式(2.1)// 读取样式 // jQuery $el.css(color); // Native // 注意:原文档指出此处存在一个已知坑——当 style 属性值为 auto 时,该方式会返回 auto const win el.ownerDocument.defaultView; // 第二个参数传 null 表示不获取伪元素样式 win.getComputedStyle(el, null).color; // 设置样式 // jQuery $el.css({ color: #ff0011 }); // Native el.style.color #ff0011;批量设置多个样式时,原文档建议参考 oui-dom-utils 包中的setStyles方法实现(该包为 OneUI 工具库,仓库中未包含其源码,此处仅按文档表述给出指引)。类名操作则与classList一一对应:// 添加类 // jQuery $el.addClass(className); // Native el.classList.add(className); // 移除类 // jQuery $el.removeClass(className); // Native el.classList.remove(className); // 是否包含类 // jQuery $el.hasClass(className); // Native el.classList.contains(className); // 切换类 // jQuery $el.toggleClass(className); // Native el.classList.toggle(className);3.2 Width Height(2.2)理论上 width 与 height 的处理方式相同,以 height 为例:// 窗口高度 $(window).height(); // 不含滚动条,行为与 jQuery 一致 window.document.documentElement.clientHeight; // 含滚动条 window.innerHeight; // 文档高度 // jQuery $(document).height(); // Native document.documentElement.scrollHeight; // 元素高度(精确到内容区,需扣除边框与内边距) // jQuery $el.height(); // Native function getHeight(el) { const styles this.getComputedStyles(el); const height el.offsetHeight; const borderTopWidth parseFloat(styles.borderTopWidth); const borderBottomWidth parseFloat(styles.borderBottomWidth); const paddingTop parseFloat(styles.paddingTop); const paddingBottom parseFloat(styles.paddingBottom); return height - borderBottomWidth - borderTopWidth - paddingTop - paddingBottom; } // 精确到整数(border-box 时即 height;content-box 时为 height padding border) el.clientHeight; // 精确到小数(border-box 时即 height;content-box 时为 height padding border) el.getBoundingClientRect().height;3.3 Position Offset(2.3)// Position(相对最近定位祖先) // jQuery $el.position(); // Native { left: el.offsetLeft, top: el.offsetTop } // Offset(相对文档) // jQuery $el.offset(); // Native function getOffset(el) { const box el.getBoundingClientRect(); return { top: box.top window.pageYOffset - document.documentElement.clientTop, left: box.left window.pageXOffset - document.documentElement.clientLeft }; }3.4 Scroll Top(2.4)// jQuery $(window).scrollTop(); // Native (document.documentElement document.documentElement.scrollTop) || document.body.scrollTop;双写documentElement/body是为了覆盖 quirks 模式下滚动值记录在body上的历史行为。四、DOM Manipulation:增删节点与内容读写4.1 移除节点(3.1)// jQuery $el.remove(); // Native el.parentNode.removeChild(el);4.2 Text 与 HTML(3.2 – 3.3)// 读文本 // jQuery $el.text(); // Native el.textContent; // 写文本 // jQuery $el.text(string); // Native el.textContent string; // 读 HTML // jQuery $el.html(); // Native el.innerHTML; // 写 HTML // jQuery $el.html(htmlString); // Native el.innerHTML htmlString;4.3 Append / Prepend(3.4 – 3.5)append在父元素的最后一个子节点之后插入新子元素:// jQuery $el.append(div idcontainerhello/div); // Native let newEl document.createElement(div); newEl.setAttribute(id, container); newEl.innerHTML hello; el.appendChild(newEl);prepend则是插入到第一个子节点之前:// jQuery $el.prepend(div idcontainerhello/div); // Native let newEl document.createElement(div); newEl.setAttribute(id, container); newEl.innerHTML hello; el.insertBefore(newEl, el.firstChild);4.4 insertBefore / insertAfter(3.6 – 3.7)// 插入到目标元素之前 // jQuery $newEl.insertBefore(queryString); // Native const target document.querySelector(queryString); target.parentNode.insertBefore(newEl, target); // 插入到目标元素之后 // jQuery $newEl.insertAfter(queryString); // Native const target document.querySelector(queryString); target.parentNode.insertBefore(newEl, target.nextSibling);注意insertAfter没有原生同名 API,惯用技巧是把新节点插入到目标兄弟节点之前。五、Ajax:用 fetch 家族替代 $.ajax文档对 Ajax 一节的结论非常简洁:以fetch与fetch-jsonp两个库替代 jQuery 的 Ajax 能力(原文档分别指向 fetch-ie8 与 fetch-jsonp 两个兼容封装,前者提供面向 IE8 的兼容)。对于需要 JSONP 回调的跨域接口,fetch本身无法完成,fetch-jsonp正是其补充。六、Events:addEventListener 与 CustomEvent若需要带命名空间 事件委托的完整事件体系,文档建议直接参考 OneUI 的oui-dom-events库(仓库内不含其源码,此处按文档表述给出指引)。基础绑定/解绑/触发的替代如下:// 5.1 绑定 // jQuery $el.on(eventName, eventHandler); // Native el.addEventListener(eventName, eventHandler); // 5.2 解绑 // jQuery $el.off(eventName, eventHandler); // Native el.removeEventListener(eventName, eventHandler); // 5.3 触发自定义事件(附带数据) // jQuery $(el).trigger(custom-event, {key1: data}); // Native if (window.CustomEvent) { const event new CustomEvent(custom-event, {detail: {key1: data}}); } else { const event document.createEvent(CustomEvent); event.initCustomEvent(custom-event, true, true, {key1: data}); } el.dispatchEvent(event);else分支的createEvent/initCustomEvent是对不支持new CustomEvent的旧浏览器(即 IE 10 档)的降级处理,与全文IE 10的兼容目标保持一致。七、Utilities:常用工具函数替代// 6.1 数组判断 // jQuery $.isArray(range); // Native Array.isArray(range); // 6.2 去首尾空白 // jQuery $.trim(string); // Native string.trim(); // 6.3 对象合并(Extend) // Native 侧使用 Object.assign;老浏览器可用 ljharb/object.assign polyfill // jQuery $.extend({}, defaultOpts, opts); // Native Object.assign({}, defaultOpts, opts); // 6.4 节点包含关系 // jQuery $.contains(el, child); // Native el ! child el.contains(child);contains处的el ! child前置判断值得留意:按 DOM 规范,Node.contains对自身返回true,而 jQuery 的$.contains要求严格祖先关系,加上这个不等式判断才能对齐语义。八、浏览器支持范围原文档给出的支持矩阵如下:ChromeFirefoxIEOperaSafariLatest ✔Latest ✔10 ✔Latest ✔6.1 ✔这意味着上文所有替代写法都以 IE 10 为兼容性下限;个别 API(如el.dataset)文档特别标注需 IE 11,使用时需按目标环境取舍。九、配套测试:如何用 Karma 验证这些替代写法该夹具目录内附带完整的自动化测试,可用来逐条验证本文的替代写法与 jQuery 行为等价:test/README.md 给出两条命令:npm run test单次运行全部测试,npm run tdd以 TDD 模式持续运行;package.json 中对应脚本为karma start --single-run、karma start --auto-watch --no-single-run,另有test-cov(带 coverage 报告)与lint(eslint 检查 src 与 test 目录);测试栈为 Karma Mocha Chai,构建走 webpack Babel(preset-es2015 与 stage-0),并引入jquery作为对照依赖,配合 karma.conf.js 使用;测试文件按文档章节划分:query.spec.js、css.spec.js、dom.spec.js 与 utilities.spec.js;以 query.spec.js 的 1.0 用例为例:在beforeEach中注入一段含嵌套ul的 HTML,然后断言$(li.item[data-rolered])与document.querySelectorAll(li.item[data-rolered])数量一致(均为 2)且逐元素相等;afterEach中又用el.parentNode.removeChild(el)清理——恰好呼应了本文 DOM Manipulation 一节的原生写法。十、在 Atom 仓库中定位这份文档如果你是在 Atom 仓库里打开这份 README,以下路径构成完整的上下文链路:文档主体:spec/fixtures/git/repo-with-submodules/You-Dont-Need-jQuery/README-id.md;其余语言版本(印尼语版文档的 Translation 章节列出的译文,均已转换为仓库根相对路径):README.ko-KR.md、README.zh-CN.md、README-my.md、README.pt-BR.md、README-vi.md、README-ru.md、README-tr.md;submodule 声明:spec/fixtures/git/repo-with-submodules/.gitmodules 与 git.git/config;消费方测试:spec/project-spec.js 中.observeRepositories()用例,验证含 submodule 的仓库被atom.project正确发现、其 master 引用目标与夹具中的提交一致。综合来看,这份文档的价值在于把jQuery 到原生 API的迁移拆成了可直接对照的代码对:每一节都是一个jQuery 写法 → Native 写法的双栏映射,并针对 IE 10 给出了matchesSelector多前缀、initCustomEvent降级、dataset版本门槛等细节;配合目录内自带的 Karma 测试,迁移后可以在真实浏览器里逐条验证等价性。【免费下载链接】atom:atom: The hackable text editor项目地址: https://gitcode.com/gh_mirrors/at/atom创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考