虽然我已经作网站建设工做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。