拋棄jQuery,深入原生的JavaScript

jopen 10年前發布 | 60K 次閱讀 jQuery Ajax框架

雖然我已經做網站建設工作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。


來自:http://ourjs.com/detail/535556a1ed9add0e26000002

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!