如何使用原生方法来替代jQuery的常用方法

今天在逛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);
// 原生替代方法,不支持IE
el.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);
// 添加class
el.classList.remove(className);
// 移除class
el.classList.contains(className);
// 包含class
el.classList.toggle(className);
// toggle class

Position & Offset

// jQuery
$el.position();
// 原生替代方法
{ left: el.offsetLeft, top: el.offsetTop }
// 获取offset
function 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);
// 原生替代方法设置html
el.innerHTML = htmlString;

append方法

// jQuery:DOMString 和 Node 对象的统一语法
$parent.append(newEl | \'
Hello World
\');
// 原生方法:不同语法
parent.insertAdjacentHTML(\'beforeend\', \'
Hello World
\');
parent.appendChild(newEl);
// Native (ES6-way):统一语法
parent.append(newEl | \'
Hello World
\');

prepend方法

// jQuery:DOMString 和 Node 对象的统一语法
$parent.prepend(newEl | \'
Hello World
\');
// 原生方法:不同语法
parent.insertAdjacentHTML(\'afterbegin\', \'
Hello World
\');
parent.insertBefore(newEl, parent.firstChild);
//  Native (ES6-way):统一语法
parent.prepend(newEl | \'
Hello World
\');

insertBefore

// jQuery
$newEl.insertBefore(selector);
//  原生方法
el.insertAdjacentHTML(\'beforebegin \', \'
Hello World
\');
// 原生 (元素)
const el = document.querySelector(selector);
if (el.parentNode) {
  // 父元素的insertBefore
  el.parentNode.insertBefore(newEl, el);
}

insertAfter

// jQuery
$newEl.insertAfter(selector);
// 原生 (HTML字符串)
el.insertAdjacentHTML(\'afterend\', \'
Hello World
\');
// 原生 (元素)
const el = document.querySelector(selector);
if (el.parentNode) {
  // 利用父元素的insertBefore
  el.parentNode.insertBefore(newEl, el.nextSibling);
}

wrap

// jQuery
$(\'.inner\').wrap(\'
\');
//  原生方法
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(\'
\');

//原生替代方法- >= 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的方式。

// jQuery
function 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#L363
el.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);
// 原生替代方法:fadeOut
function 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\';
}
// 原生替代方法:fadeIn
function 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\';
// 原生替代方法:slideUp
el.style.height = \'0px\';
// 原生替代方法:slideDown
el.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

内容出处:,

声明:本网站所收集的部分公开资料来源于互联网,转载的目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。如果您发现网站上有侵犯您的知识产权的作品,请与我们取得联系,我们会及时修改或删除。文章链接:http://www.yixao.com/procedure/30272.html

发表评论

登录后才能评论