各人好,很快乐又碰头了,我是"前端‬进阶‬",由我带着各人一路存眷前端前沿、深切前端底层手艺,各人一路前进,也欢送各人存眷、点赞、保藏、转发!

请别再用 jQuery 了!  第1张

前端‬进阶

今天在逛Github时无意间发现一个仓库《YOU Dont Need jQuery》。翻开来看,发现很有意思!并且做者列举了良多jQuery常用的办法以及原生实现,因而那篇文章将以此展开。话不多说,间接起头!

关于更多jQuery的讨论能够阅读文末我的另一篇文章《18岁了!老伴计jQuery过的如何?》

媒介

前端开展敏捷,现代阅读器已经实现了大量足以用于消费的 DOM/BOM API。 没必要从头起头进修 jQuery 来停止 DOM 操做或其他类型事务处置。 与此同时,因为 React、Angular 和 Vue 等前端库的普及,间接操做 DOM 并不是更好的办法。 该项目总结了 jQuery 办法的原生 Javascript 替代办法,并撑持 IE 10+。

ℹ️ 留意:

本文并不是否认jQuery,而是切磋能否能够在没有jQuery的场景下实现功用原生实现计划并不是在所有场景下都完全等效jQuery,建议做阅读器兼容性测试。1.jQuery选择器

能够利用 document.querySelector 或 document.querySelectorAll 取代常见的jQuery选择器,如 class、id 或 attribute。 差别之处在于:

document.querySelector 返回第一个婚配的元素document.querySelectorAll 将所有婚配的元素做为 NodeList 返回。 能够利用 Array.PRototyPE.slice.call(document.querySelectorAll(selector)); 将其转换为 Array。若是没有婚配的元素,jQuery 和 document.querySelectorAll 将返回 [],而 document.querySelector 将返回 null。

留意:document.querySelector 和 document.querySelectorAll 十分慢,若是想获得更佳性能,能够利用 document.getElementById、document.getElementsByClassName 或 document.getElementsByTagName办法。

好比下面的jQuery办法都能够考虑利用原生办法来替代:

// jQuery$('selector');// 原生替代办法document.querySelectorAll('selector');

class选择器:

// jQuery$('.class');// 原生替代办法document.querySelectorAll('.class');// 原生替代办法document.getElementsByClassName('class');

id选择器:

// jQuery$('#id');// 原生替代办法document.querySelector('#id');// 或者document.getElementById('id');// 或者window['id']

属性选择器:

// jQuery$('a[target=_blank]');// 原生替代办法document.querySelectorAll('a[target=_blank]');

子级选择器:

// jQuery$el.find('li');// 原生替代办法el.querySelectorAll('li');

所有兄弟元素

// jQuery$el.siblings();// 原生替代办法 - Edge13+[...el.parentNode.children].filter((child) => child !== el);// 原生替代办法- Edge13+Array.from(el.parentNode.children).filter((child) => child !== el);// 原生替代办法 - IE10+Array.prototype.filter.call(el.parentNode.children, (child) => child !== el);

前面兄弟元素:

// jQuery$el.prev();// 原生替代办法el.previousElementSibling;

后面兄弟元素:

// jQuery$el.next();// 原生替代办法el.nextElementSibling;

所有前面兄弟元素:

// jQuery$el.prevAll($filter);// 原生替代办法function getPreviousSiblings(elem, filter) { var sibs = []; while (elem = elem.previousSibling) { if (elem.nodeType === 3) continue; // 忽略text类型 if (!filter || filter(elem)) sibs.push(elem); } return sibs;}

所有后面兄弟元素:

// jQuery$el.nextAll($filter);// 原生替代办法function getNextSiblings(elem, filter) { var sibs = []; var nextElem = elem.parentNode.firstChild; do { if (nextElem.nodeType === 3) continue; // 忽略文本 if (nextElem === elem) continue; // 忽略本身 if (nextElem === elem.nextElementSibling) { if (!filter || filter(elem)) { sibs.push(nextElem); elem = nextElem; } } } while(nextElem = nextElem.nextSibling) return sibs; }

closest办法(通过供给的选择器返回第一个婚配的元素,从当前元素向上遍历它在 DOM 树中的祖先):

// jQuery$el.closest(selector);// 原生替代办法,不撑持IEel.closest(selector);// 原生替代办法 - IE10+function closest(el, selector) { const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; while (el) { // while轮回不断往上寻找 if (matchesSelector.call(el, selector)) { return el; } else { el = el.parentElement; } } return null;}

jQuery的parentsUntil办法:

// jQuery$el.parentsUntil(selector, filter);// 原生替代办法function parentsUntil(el, selector, filter) { const result = []; const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; // 利用matchs办法 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.jQuery的CSS & Style操做办法获取/设置Style// jQuery$el.css('color');// 原生替代办法// 留意: 已知错误,若是款式值为“auto”,将返回“auto”const win = el.ownerDocument.defaultView;// null 暗示不返回伪款式win.getComputedStyle(el, null).color;// 原生替代办法el.style.color = '#f01';添加/移除/判断/toggle类// jQuery$el.adDClass(className);// 原生替代办法el.classList.add(className);// 添加classel.classList.remove(className);// 移除classel.classList.contains(className);// 包罗classel.classList.toggle(className);// toggle classPosition & Offset// jQuery$el.position();// 原生替代办法{ left: el.offsetLeft, top: el.offsetTop }// 获取offsetfunction getOffset (el) { const box = el.getBoundingClientRect(); return { // 连结兼容 top: box.top + window.pageYOffset - document.documentElement.clientTop, left: box.left + window.pageXOffset - document.documentElement.clientLeft };}ScrollTop// jQuery$(window).scrollTop();// 原生替代办法(document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop;3.DOM操做移除元素// jQuery$el.remove();// 原生替代办法el.parentNode.removeChild(el);get/set HTML// jQuery$el.html();// 原生替代办法el.innerHTML;// jQuery设置html$el.html(htmlString);// 原生替代办法设置htmlel.innerHTML = htmlString;append办法// jQuery:DOMString 和 Node 对象的同一语法$parent.append(newEl | '<div id="container">Hello World</div>');// 原生办法:差别语法parent.insertAdjacentHTML('beforeend', '<div id="container">Hello World</div>');parent.appendChild(newEl);// Native (ES6-way):同一语法parent.append(newEl | '<div id="container">Hello World</div>');prepend办法// jQuery:DOMString 和 Node 对象的同一语法$parent.prepend(newEl | '<div id="container">Hello World</div>');// 原生办法:差别语法parent.insertAdjacentHTML('afterbegin', '<div id="container">Hello World</div>');parent.insertBefore(newEl, parent.firstChild);// Native (ES6-way):同一语法parent.prepend(newEl | '<div id="container">Hello World</div>');insertBefore// jQuery$newEl.insertBefore(selector);// 原生办法el.insertAdjacentHTML('beforebegin ', '<div id="container">Hello World</div>');// 原生 (元素)const el = document.querySelector(selector);if (el.parentNode) { // 父元素的insertBefore el.parentNode.insertBefore(newEl, el);}insertAfter// jQuery$newEl.insertAfter(selector);// 原生 (HTML字符串)el.insertAdjacentHTML('afterend', '<div id="container">Hello World</div>');// 原生 (元素)const el = document.querySelector(selector);if (el.parentNode) { // 操纵父元素的insertBefore el.parentNode.insertBefore(newEl, el.nextSibling);}wrap// jQuery$('.inner').wrap('<div class="wrapper"></div>');// 原生办法Array.from(document.querySelectorAll('.inner')).forEach((el) => { const wrapper = document.createElement('div'); wrapper.className = 'wrapper'; el.parentNode.insertBefore(wrapper, el); // 父元素的insertBefore wrapper.appendChild(el);});replaceWith// jQuery$('.inner').replaceWith('<div class="outer"></div>');//原生替代办法- >= Edge17+Array.from(document.querySelectorAll('.inner')).forEach((el) => { const outer = document.createElement('div'); outer.className = 'outer'; el.replaceWith(outer);});// 原生替代办法Array.from(document.querySelectorAll('.inner')).forEach((el) => { const outer = document.createElement('div'); outer.className = 'outer'; // 挪用父元素的replaceChild el.parentNode.replaceChild(outer, el);});4.jQuery的Ajax恳求

Fetch API 是替代 XMLHttpRequest 施行 ajax 的新尺度。 它适用于 Chrome 和 Firefox,您能够利用 polyfills 使其适用于旧版阅读器。

在 IE9+ 上能够利用 github/fetch 或在 IE8+ 上利用 fetch-ie8,fetch-jsonp 来发出 JSONP 恳求。

// jQuery$(selector).load(url, completeCallback)// 原生替代办法fetch(url).then(data => data.text()).then(data => { document.querySelector(selector).innerHTML = data}).then(completeCallback)// POST办法await fetch('/my/url', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data)});5.事务DOMContentLoaded // jQuery $(document).ready(eventHandler); // 原生替代办法 // 查抄 DOMContentLoaded 能否已经完成 if (document.readyState !== 'loading') { eventHandler(); } else { document.addEventListener('DOMContentLoaded', eventHandler); } // 原生替代办法 // 示例 2 - 三元运算符 // 异步查抄 DOMContentLoaded 能否已经完成 (async function() { (document.readyState !== 'loading') ? eventHandler() : document.addEventListener('DOMContentLoaded', function() { eventHandler(); // 事务处置函数 }); })(); // 原生替代办法 // 示例 3 - 三元运算符 //非异步查抄 DOMContentLoaded 能否已经完成 (function() { (document.readyState !== 'loading') ? eventHandler() : document.addEventListener('DOMContentLoaded', function() { eventHandler(); // 事务处置函数 }); })();绑定/移除事务// jQuery$el.on(eventName, eventHandler);// 添加:原生替代办法el.addEventListener(eventName, eventHandler);// jQuery$el.off(eventName, eventHandler);// 移除:原生替代办法el.removeEventListener(eventName, eventHandler);trigger事务// jQuery$(el).trigger('custom-event', {key1: 'data'});// 通过CustomEvent原生办法替代jQuery的trigger办法 if (window.CustomEvent) { const event = new CustomEvent('custom-event', {detail: {key1: 'data'}});} else { const event = document.createEvent('CustomEvent'); // 构造CustomEvent实例 event.initCustomEvent('custom-event', true, true, {key1: 'data'});}// 挪用dispatchEvent发布事务el.dispatchEvent(event);6.jQuery的Promise办法

Promise 暗示异步操做的最末成果。 jQuery 有本身的体例来处置Promise。本机 JavaScript 实现了一个精简的最小 API 来按照 Promises/A+ 标准处置 promises。

done, fail, always

done 在 promise 被resolve时被挪用,fail 在 promise 被reject时被挪用,当 promise 被处理或被回绝时always被挪用。

// jQuery$promise.done(doneCallback).fail(failCallback).always(alwaysCallback)// Promise原生替代办法promise.then(doneCallback, failCallback).then(alwaysCallback, alwaysCallback)when

when 用于处置多个promise。当所有promise都resolve时,它将resolve,若是任何一个被回绝,它就会回绝。

// jQuery$.when($promise1, $promise2).done((promise1Result, promise2Result) => {});// when原生替代办法Promise.all([$promise1, $promise2]).then([promise1Result, promise2Result] => {});Deferred

是jQuery一种创建Promise的体例。

// jQueryfunction asyncFunc() { const defer = new $.Deferred(); setTimeout(() => { if(true) { defer.resolve('some_value_computed_asynchronously'); } else { defer.reject('failed'); } }, 1000); return defer.promise();}// 原生替代办法function asyncFunc() { return new Promise((resolve, reject) => { setTimeout(() => { if (true) { resolve('some_value_computed_asynchronously'); } else { reject('failed'); } }, 1000); });}// 通过实现jQuery的Deferred// 留意:jQuery的Deferred是它实现Promise才能的根底function defer() { const deferred = {}; const promise = new Promise((resolve, reject) => { deferred.resolve = resolve; deferred.reject = reject; }); deferred.promise = () => { return promise; }; return deferred;}// asyncFunc 函数伺机挪用resolve/reject办法function asyncFunc() { const defer = defer(); setTimeout(() => { if(true) { // resolve defer.resolve('some_value_computed_asynchronously'); } else { // reject defer.reject('failed'); } }, 1000); return defer.promise();}7.jQuery实现Animation动画Show & Hide// jQuery$el.show();$el.hide();// 原生替代办法// 更多信息参考链接:https://github.com/oneuijs/oui-dom-utils/blob/master/src/index.js#L363el.style.display = ''|'inline'|'inline-block'|'inline-table'|'block';el.style.display = 'none';toggle// jQuery$el.toggle();// 原生替代办法if (el.ownerDocument.defaultView.getComputedStyle(el, null).display === 'none') { // 设置display属性值 el.style.display = ''|'inline'|'inline-block'|'inline-table'|'block';} else { el.style.display = 'none';}FadeIn & FadeOut// jQuery$el.fadeIn(3000);$el.fadeOut(3000);// 原生替代办法:fadeOutfunction fadeOut(el, ms) { if (ms) { el.style.transition = `opacity ${ms} ms`; el.addEventListener( 'transitionend', // 动画完毕 function(event) { el.style.display = 'none'; }, false ); } el.style.opacity = '0';}// 原生替代办法:fadeInfunction fadeIn(elem, ms) { elem.style.opacity = 0; if (ms) { let opacity = 0; const timer = setInterval(function() { // setInterval挪用 opacity += 50 / ms; if (opacity >= 1) { clearInterval(timer); opacity = 1; } // 设置opacity elem.style.opacity = opacity; }, 50); } else { elem.style.opacity = 1; }}SlideUp & SlideDown// jQuery$el.slideUp();$el.slideDown();// 原生替代办法const originHeight = '100px';el.style.transition = 'height 3s';// 原生替代办法:slideUpel.style.height = '0px';// 原生替代办法:slideDownel.style.height = originHeight;8.本文总结

本文次要和各人介绍jQuery的良多常用办法若何利用原生办法来替代,好比:选择器、动画、Promise、事务、Ajax、DOM操做等等,从而引出“你可能不需要jQuery”的结论。当然,每小我城市有差别的观点。若是你觉得引入jQuery的收益关于你的项目很大,那么你仍是能够对峙利用它。

同时,文末的参考材料供给了大量优良文档以供进修,若是有兴趣能够自行阅读。若是各人有什么疑问欢送在评论区留言。

参考材料

https://github.com/camsong/You-Dont-Need-jQuery

http://www.kehuanxianshi.com/work/JavaScript/now-ever-might-not-need-jquery.html

https://www.toutiao.com/article/7198323737454723622/

https://youmightnotneedjquery.com/

https://medium.com/@navneet.sahota/you-dont-need-jquery-ec070bc75238

图片版权:来自Navneet Singh的文章《You don’t need jQuery》!