抛弃jQuery,深入原生的JavaScript

虽然我已经做网站建设工作10多年了,但我从最近3年才开始更多地学习如何更好的将纯JavaScript用于工作中,而不总是将jQuery考虑在第一位。现在我每天学习很多东西。这个过程让我觉得Adtile的JavaScript SDK 更像是在创建一个开源工程,而不是“具体的工作”,不得不说,我很喜欢那样。

今天,我准备将在过去几年学到的一些基础东西与大家一起分享,这将可能帮你深入纯 JavaScript的世界,让你能更简单的做出决定——jQuery在你下个工程中是否需要。

逐步增强

虽然像jQuery这样的库有助于解决许多浏览器之间不兼容的问题,但当你一旦开始使用纯JavaScript来完成所有工作的时候你确实会变得对他们很熟悉。为了避免写包含浏览器修改和只能解决浏览器兼容问题的JavaScript代码,我建议使用特征检测只将更现代化的浏览器作为目标来培养逐步增强的经验。这并不意味着从像IE7这样的浏览器上得不到任何东西,这只能说明在JavaScript没有增强的情况下他们得到一个更基础的经验。

我们是怎么做的

我们有一个叫做”feature.js”的分离的JavaScript部分,它拥有所有的功能测试。真实的测试列表比这长多了,但让我们稍晚点再回到这问题吧。为了消除一些老浏览器的不兼容,我们使用如下两个测试:

var feature = {
  addEventListener : !!window.addEventListener,
  querySelectorAll : !!document.querySelectorAll,
};

然后,在主应用程序部分,我们检测这些特性是否能被下面例子中简单的“if”语句支持。如果不被支持,那么浏览器将不会执行如下的任何代码:

if (feature.addEventListener && feature.querySelectorAll) {
  this.init();
}

这两个测试确保我们在JavaScript中使用CSS选择器时有本地方法(querySelectorAll)可用,添加和删除事件的简便方法(addEventListener)且浏览器标准支持比IE8的好。阅读有关这个方法的更多内容请访问BBC blog的 “Cutting the mustard 文章。

浏览器支持

这儿有一个我们测试的哪些浏览器支持这个特性,且日后能保持运行JavaScript的粗略列表:

  • IE9+
  • Firefox 3.5+
  • Opera 9+
  • Safari 4+
  • Chrome 1+
  • iPhone and iPad iOS1+
  • Android phone and tablets 2.1+
  • Blackberry OS6+
  • Windows 7.5+
  • Mobile Firefox
  • Opera Mobile

基础原生JavaScript方法

让我们开始关注与jQuery相比,最基础且需求频繁的功能在纯JavaScript中是如何工作的。对于每个例子,我都打算提供jQuery和纯JavaScript两种方法。

Document Ready 事件

在jQuery中,你们中的许多人可能过去常常像这样使用 document.ready :

$(document).ready(function() {
  // Code
});

但是你知道,你可以将所有的JavaScript放在页面的底端,但他们确实是一回事吗?JavaScript同样拥有一个DOM内容加载事件的侦听器,而不是使用jQuery的document.ready:

document.addEventListener("DOMContentLoaded", function() {
  // Code
}, false);

选择器API

JavaScript的本地选择器API非常优秀。它对CSS选择器是有用的且jQuery提供的非常类似。如果你过去经常在jQuery中这样写:

var element = $("div");

现在你可以用如下的语句来替代:

var element = document.querySelector("div");

或者选择所有div的某些内部容器:

var elements = document.querySelectorAll(".container div");

你也可以针对特定元素进行查询来找到它的子元素:

var navigation = document.querySelector("nav");
var links = navigation.querySelectorAll("a");

很简单,容易理解且现在不需要太多的代码,不是吗?更远一步来说,我们甚至可以自己建一个小型的JavaScript库来进行简单的DOM查询。以下是Andrew Lunny已经想出来的一些东西

// This gives us simple dollar function and event binding
var $ = document.querySelectorAll.bind(document);
Element.prototype.on = Element.prototype.addEventListener;

// This is how you use it
$(".element")[0].on("touchstart", handleTouch, false);

遍历DOM

用纯JavaScript来遍历DOM比起用jQuery来说有一些困难。但也不是太困难。下面是一些简单的例子:

// Getting the parent node
var parent = document.querySelector("div").parentNode;
// Getting the next node
var next = document.querySelector("div").nextSibling;
// Getting the previous node
var next = document.querySelector("div").previousSibling;
// Getting the first child element
var child = document.querySelector("div").children[0];
// Getting the last child
var last = document.querySelector("div").lastElementChild;

添加和删除样式名(class name)

使用jQuery,添加、删除和检查一个元素是否有确定的类是很简单的事。用纯JavaScript会有一些复杂,但也不是太复杂。给元素一个叫做“foo”的类且替换目前所有的类:

// Select an element
var element = document.querySelector(".some-class");
// Give class "foo" to the element
element.className = "foo";

在不替换目前类的前提下增加类:

element.className += " foo";

从html元素中移除”no-js”类且用”js”来替代:

<html class="no-js">
<head>
  <script>
    document.documentElement.className = "js";
  </script>

这相当简单,对不对?下一步,只移除某些类稍微有点复杂。我一直在单独部分使用这个叫做util.js的小助手函数。它有两个参数:元素和你想移除的类:

// removeClass, takes two params: element and classname
function removeClass(el, cls) {
  var reg = new RegExp("(\\s|^)" + cls + "(\\s|$)");
  el.className = el.className.replace(reg, " ").replace(/(^\s*)|(\s*$)/g,"");
}

然后,在主应用程序部分,我一直像这样使用:

removeClass(element, "foo");

如果针对某些类你同样想检查一个元素,那么就像jQuery的hasClass一样。你可能需要把这些代码加入到你的utils工具中:

// hasClass, takes two params: element and classname
function hasClass(el, cls) {
  return el.className && new RegExp("(\\s|^)" + cls + "(\\s|$)").test(el.className);
}

然后可以这样使用:

// Check if an element has class "foo"
if (hasClass(element, "foo")) {

  // Show an alert message if it does
  alert("Element has the class!");
}

HTML5 的  classList API 简介

如果你只需要支持像IE10+,Chrome,FireFox,Opera和Safari这样较现代的浏览器,那么你可以开始使用HTML5的classList功能,它让增加和删除类变得更简单。

这是我在我们最新的开发者文档中最终做的事,随着功能的开发,这更像是UI的加强,且如果这不是现在的,其实际上并不是能打破经验的一些东西。

通过下面简单的”if”语句,你可以检测出浏览器是否支持这个功能:

if ("classList" in document.documentElement) {
  // classList is supported, now do something with it
}

用classList来添加、删除、转换类:

// Adding a class
element.classList.add("bar");
// Removing a class
element.classList.remove("foo");
// Checking if has a class
element.classList.contains("foo");
// Toggle a class
element.classList.toggle("active");

使用classList的另一个好处是它比使用原始的类名属性表现得更好。如果你有像这样的元素:

<div id="test" class="one two three"></div>

你想操作哪一个:

var element = document.querySelector("#test");
addClass(element, "two");
removeClass(element, "four");

这些被类名属性读和写的方法将触发浏览器重绘。但这并不是我们是否应该用相应的classList方法的情况:

var element = document.querySelector("#test");
element.classList.add("two");
element.classList.remove("four");

用classList之后,最基本的类名属性仅在必要的时候进行更改。添加一个已经存在的类和移除一个不存在的类时,根本没有牵涉到类名属性,这意味着我们刚刚避免了两次重绘。

事件监听器

添加和移除一个元素的事件监听器在纯JavaScript和jQuery中都一样比较简单。当你必须要添加多个事件监听器时,事情就变得有点复杂了,但我会详细解释给你的。最简单的一个例子,当你单击一个元素时弹出一个警告信息,如下代码所述:

element.addEventListener("click", function() {
  alert("You clicked");
}, false);

为了让页面的所有元素都实现这个功能,我们必须依次重复每个元素且给他们添加事件监听器:

// Select all links
var links = document.querySelectorAll("a");

// For each link element
[].forEach.call(links, function(el) {

  // Add event listener
  el.addEventListener("click", function(event) {
    event.preventDefault();
    alert("You clicked");
  }, false);

});

JavaScript有关事件监听器一个最伟大的功能就是“addEventListener” 能携带一个作为第二个参数的对象,这将会让它自动的寻找一个叫做“handleEvent”的方法然后调用它。Ryan Seddon在它的文章中已经彻底地介绍了这个方法,所以我只打算给一个最简单的例子,你可以通过在它的博客中学到更多的东西:

var object = {
  init: function() {
    button.addEventListener("click", this, false);
    button.addEventListener("touchstart", this, false);
  },
  handleEvent: function(e) {
    switch(e.type) {
      case "click":
        this.action();
        break;
      case "touchstart":
        this.action();
        break;
    }
  },
  action: function() {
    alert("Clicked or touched!");
  }
};

// Init
object.init();

操作DOM

用纯JavaScript来操作DOM刚开始听起来就像一个可怕的想法,但比使用jQuery其实它并没有复杂多少。下面,我们会有一个例子,选择DOM的元素,克隆它,用JavaScript来操作克隆的样式,然后用被操纵的东西来替代原始的元素。

// Select an element
var element = document.querySelector(".class");

// Clone it
var clone = element.cloneNode(true);

// Do some manipulation off the DOM
clone.style.background = "#000";

// Replaces the original element with the new cloned one
element.parentNode.replaceChild(clone, element);

在DOM中,如果除了附加在<body>中新创建div,你不想替代任何东西,那么你可以这样做:

document.body.appendChild(clone);

如果你觉得你想了解更多不同的DOM方法,我建议你可以拜读一下 Peter-Paul Koch的DOM Core tables

进一步深入

在这儿我想再多分享两个我最近发现的先进技术。这些都是我们在创建Adtile的时候需要的功能,因此你也会觉得它们很有用。

在JS中决定响应图片的最大宽度

这是我最爱的之一,且如果你需要用JavaScript操作流体图片时这非常有用。由于浏览器默认返回当前被调整过大小的图片,我们必须要想一些其它的办法。幸运的是,现代浏览器目前已有解决的方案了:

var maxWidth = img.naturalWidth;

这将会给我们提供最大宽度100%像素的图片,且IE9,Chrome,Firefox,Safari和Opera都支持这个方法。我们也可以保留这个特性然后通过加载图片到内存中添加老浏览器的支持:

// Get image‘s max-width:100%; in pixels
function getMaxWidth(img) {
  var maxWidth;

  // Check if naturalWidth is supported
  if (img.naturalWidth !== undefined) {
    maxWidth = img.naturalWidth;

  // Not supported, use in-memory solution as fallback
  } else {
    var image = new Image();
    image.src = img.src;
    maxWidth = image.width;
  }

  // Return the max-width
  return maxWidth;
}

你应该注意到在检查宽度前,图片必须完全被加载。这是我们一直使用的用于确定它们有尺寸的方法:

function hasDimensions(img) {
  return !!((img.complete && typeof img.naturalWidth !== "undefined") || img.width);
}

判断一个元素是否在视图窗口中

通过使用getBoundingClientRect方法,你可以获取页面中任何元素的位置。以下是一个简单的函数来表明它有多简单和多强大。这个函数有一个参数,那就是你想要检查的元素。当元素为可见时,函数将返回true:

// Determine if an element is in the visible viewport
function isInViewport(element) {
  var rect = element.getBoundingClientRect();
  var html = document.documentElement;
  return (
    rect.top >= 0 &&
    rect.left >= 0 &&
    rect.bottom <= (window.innerHeight || html.clientHeight) &&
    rect.right <= (window.innerWidth || html.clientWidth)
  );
}

上面的函数可以在给窗体添加一个”滚动”事件监听器,然后调用isInViewport()方法时使用。

结论

在你的下个项目中是否使用jQuery很大程度上取决于你所建的项目。如果你的项目需要大量的前端代码,那么你应该使用jQuery。然而,当你构建一个JavaScript插件或库时,你应该只考虑纯JavaScript。使用纯JavaScript意味着更少的请求和更少的数据加载。这同样意味着你不必强迫开发人员在他们的项目中加jQuery。

原文地址: blog.adtile.me

时间: 2024-10-12 19:04:32

抛弃jQuery,深入原生的JavaScript的相关文章

抛弃jQuery 深入原生的JavaScript

学习如何更好的将纯JavaScript用于工作中,而不总是将jQuery考虑在第一位.现在我每天学习很多东西.这个过程让我觉得Adtile的JavaScript SDK 更像是在创建一个开源工程,而不是"具体的工作",不得不说,我很喜欢那样. 今天,我准备将在过去几年学到的一些基础东西与大家一起分享,这将可能帮你深入纯 JavaScript的世界,让你能更简单的做出决定--jQuery在你下个工程中是否需要. 逐步增强 虽然像jQuery这样的库有助于解决许多浏览器之间不兼容的问题,但

抛弃jQuery,拥抱原生JavaScript

前端发展很快,现代浏览器原生 API 已经足够好用.我们并不需要为了操作 DOM.Event 等再学习一下 jQuery 的 API.同时由于 React.Angular.Vue 等框架的流行,直接操作 DOM 不再是好的模式,jQuery 使用场景大大减少.因此我们项目组在双十一后抽了一周时间,把所有代码中的 jQuery 移除.下面总结一下: Why not jQuery 1. 模式变革 jQuery 代表着传统的以 DOM 为中心的开发模式,但现在复杂页面开发流行的是以 React 为代表

异步处理XML异步数据——以原生的JavaScript与jQuery中的$.ajax()为例

此文档解决以下问题: 一.原生的JavaScript从服务器端输出XML格式数据 1.XMLHttpRequest对象的运用 XMLHttpRequest对象的open()方法 XMLHttpRequest对象的send()方法 XMLHttpRequest对象的onreadystatechange事件运用 XMLHttpRequest对象的readyState属性 XMLHttpRequest对象的status属性 XMLHttpRequest对象的responseXML属性 2.docume

什么是原生的javascript

在www.cocos.com的cocos2d-js的介绍中写道“Cocos2d-JS 是跨全平台的游戏引擎,采用原生JavaScript语言,可发布到包括Web平台,iOS,Android,Windows Phone8,Mac,Windows等平台,”. 那么什么是原生的Javascript呢? 原生态js是指遵循ECMAscript标准的javascript,不同于微软的jscript也不依赖于任何框架,依托于浏览器标准引擎的脚本语言.比如jquery是在原生态的js上集成的框架资源,使用jq

用jQuery之后,之前javascript的一些方法就不能用了吗

用jQuery之后,之前javascript的一些方法就不能用了吗? 比如$("#btn").onclick = function(){}这种用法?或者$("#btn").addEventListener("click",function(){})这种?? 难道在jQuery中,它们改为了$("#btn").click(function(){});或者$("#btn").bind("click&q

彻底抛弃 jQuery ,不然还留着过年?

我以前很喜欢 jQuery,而且说实话,我是先学jQuery,再学 JavaScript 的.所以我写这篇文章有点像是在背叛 jQuery. 我知道,关于为什么不应该用 jQuery 的文章已经汗牛充栋,但我只是想说下自己的亲身体验. 不用 jQuery 用什么? 随着 web 的发展,新技术长江后浪推前浪,前浪死在沙滩上.就像有些编程语言曾经辉煌过,现在也消失不见了.我认为 jQuery 也不例外,是时候跟 它说再见了,虽然它曾经给我们带来过美妙的编程体验. 为什么要放弃 jQuery 呢?因

Ext JS学习第十天 Ext基础之 扩展原生的javascript对象(二)

此文来记录学习笔记: 今天继续说Ext.Array,Ext.Function,Ext.Date,Ext.Error ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Ext.Ar

Ext JS学习第九天 Ext基础之 扩展原生的javascript对象

此文来记录学习笔记: •Ext对于原生的javascript对象进行了一系列的扩展,我们把他们掌握好,更能深刻的体会Ext的架构,从而对我们的web开发更好的服务, 源码位置,我们可以从开发包的这个位置找到这几个扩展的js源码: •extjs-4.1.1\src\core\src\lang\ •ExtJS扩展原生Javascript –Ext.Object –Ext.Number –Ext.String –Ext.Array –Ext.Function –Ext.Date –Ext.Error

不同浏览器(chrome,firefox,IE)在JQuery与原生JS之执行性能比较

本次测试所用浏览器为chrome(36.0.1985.125m),firefox(31.0),IE(8.0) 1. 比较JQuery在不同浏览器对于js的执行性能 计算插入20000条div节点所需时间 for(var i=0;i<20000;i++){ var divTag = document.createElement('div'); $("#chn").append(divTag); } Result: 时间单位ms chrome 584 614 593 574 596

JQuery日记6.5 Javascript异步模型(二)

mnesia在频繁操作数据的过程可能会报错:** WARNING ** Mnesia is overloaded: {dump_log, write_threshold},可以看出,mnesia应该是过载了.这个警告在mnesia dump操作会发生这个问题,表类型为disc_only_copies .disc_copies都可能会发生. 如何重现这个问题,例子的场景是多个进程同时在不断地mnesia:dirty_write/2 mnesia过载分析 1.抛出警告是在mnesia 增加dump