雖然我已經作網站建設工做10多年了,但我從最近3年纔開始更多地學習如何更好的將純JavaScript用於工做中,而不老是將jQuery考慮在第一位。如今我天天學習不少東西。這個過程讓我以爲Adtile的JavaScript SDK 更像是在建立一個開源工程,而不是「具體的工做」,不得不說,我很喜歡那樣。javascript
今天,我準備將在過去幾年學到的一些基礎東西與你們一塊兒分享,這將可能幫你深刻純 JavaScript的世界,讓你能更簡單的作出決定——jQuery在你下個工程中是否須要。css
雖然像jQuery這樣的庫有助於解決許多瀏覽器之間不兼容的問題,但當你一旦開始使用純JavaScript來完成全部工做的時候你確實會變得對他們很熟悉。爲了不寫包含瀏覽器修改和只能解決瀏覽器兼容問題的JavaScript代碼,我建議使用特徵檢測只將更現代化的瀏覽器做爲目標來培養逐步加強的經驗。這並不意味着從像IE7這樣的瀏覽器上得不到任何東西,這隻能說明在JavaScript沒有加強的狀況下他們獲得一個更基礎的經驗。html
咱們有一個叫作」feature.js」的分離的JavaScript部分,它擁有全部的功能測試。真實的測試列表比這長多了,但讓咱們稍晚點再回到這問題吧。爲了消除一些老瀏覽器的不兼容,咱們使用以下兩個測試:前端
var feature = { addEventListener : !!window.addEventListener, querySelectorAll : !!document.querySelectorAll, };
而後,在主應用程序部分,咱們檢測這些特性是否能被下面例子中簡單的「if」語句支持。若是不被支持,那麼瀏覽器將不會執行以下的任何代碼:java
if (feature.addEventListener && feature.querySelectorAll) { this.init(); }
這兩個測試確保咱們在JavaScript中使用CSS選擇器時有本地方法(querySelectorAll)可用,添加和刪除事件的簡便方法(addEventListener)且瀏覽器標準支持比IE8的好。閱讀有關這個方法的更多內容請訪問BBC blog的 「Cutting the mustard」 文章。node
這兒有一個咱們測試的哪些瀏覽器支持這個特性,且往後能保持運行JavaScript的粗略列表:jquery
讓咱們開始關注與jQuery相比,最基礎且需求頻繁的功能在純JavaScript中是如何工做的。對於每一個例子,我都打算提供jQuery和純JavaScript兩種方法。瀏覽器
在jQuery中,大家中的許多人可能過去經常像這樣使用 document.ready :app
$(document).ready(function() { // Code });
可是你知道,你能夠將全部的JavaScript放在頁面的底端,但他們確實是一回事嗎?JavaScript一樣擁有一個DOM內容加載事件的偵聽器,而不是使用jQuery的document.ready:dom
document.addEventListener("DOMContentLoaded", function() { // Code }, false);
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);
用純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;
使用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!"); }
若是你只須要支持像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以後,最基本的類名屬性僅在必要的時候進行更改。添加一個已經存在的類和移除一個不存在的類時,根本沒有牽涉到類名屬性,這意味着咱們剛剛避免了兩次重繪。
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();
用純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的時候須要的功能,所以你也會以爲它們頗有用。
這是我最愛的之一,且若是你須要用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。