14个jQuery技巧及最佳实践
如果说jQuery一个不好的地方的话,那就是他的入门门槛太低了。因此吸引了很多没有一定JavaScript知识基础当然人。造成的局面就是,一方面,这样很好,而在另一方面,也造成一种混乱的,更直接的说是令人生厌的代码(其中有些自己也写过)。但是没关系,写出让你的祖母惊愕的代码是你走向专业的开始。关键是要越过这一步,这也是本文今天要讨论的。
1. 返回jQuery对象的方法
记住大多数方法都会返回jQuery对象很关键。这一特性十分有用,允许我们使用连接特性,也是我们经常利用的。
$someDiv .attr( 'class' , 'someClass' ) .hide() .html( 'new stuff' ) |
知道总是返回jQuery对象,我们可以成倍的移除冗余的代码。例如,考虑下面的代码:
var someDiv = $( '#someDiv' ); someDiv.hide(); |
“我们”缓存”someDiv元素到本地的原因是将我们不得不遍历多次减少为一次。”
上面的代码相当好,然而达到相同的结果我们可以把两行代码结合成一行。
var someDiv = $( '#someDiv' ).hide(); |
这样,我们仍可以隐藏someDiv元素,但是方法,如我们所知,仍然返回jQuery对象–然后可以通过someDiv变量来引用他。
2. find选择器
只要你的选择器写的不是非常差,jQuery都会尽力优化你的代码,通常你不需要太过担心他们。然而,话虽如此,一些容易实现的改进仍可以一定程度上提高代码的效率。
一种办法就是,只要有可能就使用find()方法。关键是没有必要时不要强迫jQuery使用Sizzle引擎。当然,有时候不得不用–也没什么,但是,如果你不需要而外的开销,不要用它。
// 在现代浏览器中可以,尽管Sizzle的确开始"运行"了 $( '#someDiv p.someClass' ).hide(); // 适合所有的浏览器,Sizzle不会初始化 $( '#someDiv' ).find( 'p.someClass' ).hide(); |
“现代的浏览器支持QuerySelectorAll,使你可以传递类CSS选择器,不需要jQuery的帮助。jQuery本身也会检查这一功能。”
然而,较早的浏览器,比如IE6/IE7,可以理解地不支持这一特性。这意味着这些复杂的选择器会激活jQuery的所有Sizzle引擎。尽管非常出色,但是同时带来一些而外的开销。
“Sizzle是一组非常出色的代码,我可能永远也读不懂。但是他首先的得到你的选择器然后这些选择器得到的元素组装为一个数组。”
// 基本上类似下面的结果 [" #someDi', 'p']; |
然后,使用正则表达式Sizzle开始从右到左解析。这也意味着你选择器最右边的的部分应该越具体越好–例如id或标签名。
底线,当有可能时:
- 保持选择器简单
- 使用find()方法。以这种方式,而不是Sizzle,你可以使用浏览器本地的方法。
- 当使用Sizzle时,尽量优化选择器中最右边的部分。
替换上下文?
也可以在选择器中增加上下文,例如:
$( '.someElement' , '#someContainer' ).hide(); |
代码让jQuery将所有带有类someElements的元素包装为集合, 这些元素是someContainer的子元素。使用上下文是减少DOM遍历的有效方法,然而在幕后jQuery还是使用find方法。
$( '#someContainer' ) .find( '.someElements' ) .hide(); |
证据
// HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { return jQuery( context ).find( selector ); } |
不要滥用$(this)
在不知道各种DOM属性和函数时,没有必要下很容易滥用jQuery对象。例如:
$( '#someAnchor' ).click( function () { alert($( this ).attr( 'id' ) ); }); |
如果仅是要用jQuery对象得到锚点的id属性,上面的方式比较浪费。使用”原生”的JavaScript更好。
$( '#someAnchor' ).click( function () { alert( this .id); }); |
“请记住有三个属性应该总是用jQuery来访问:”src”, “href”和”style”。在较旧版本的IE中,这些属性需要使用getAttribute得到。”
证据
// jQuery 源代码 var rspecialurl = /href|src|style/; // ... var special = respecialurl.test(name); // ... var attr = !jQuery.support.hrefNormalized && notxml && special ? // 在IE中有些属性需要特别调用 elem.getAttribute( name, 2 ) : elem.getAttribute( name); |
多个重复的jQuery对象
更糟糕的是在处理过程中重复的查询DOM创建多重的jQuery对象。
$( '#elem' ).hide(); $( '#elem' ).html( 'bla' ); $( '#elem' ).otherStuff(); |
希望你已经注意到了代码有多么低效。如果没看出来,没关系我们正在学习.答案是要么用chaining实现,要么”缓存”#elem。
// 这样更好 $( '#elem' ) .hide() .html( 'bla' ) .otherStuff(); // 或者这样,如果你更喜欢的话 var elem = $( '#elem' ); elem.hide(); elem.html( 'bla' ); elem.otherStuff(); |
4. jQuery中Ready方法的简写
用jQuery来监听文档准备好被操作非常简单。
$(document).ready( function () { // ... }); |
尽管你很可能理解一个不同的,更令人迷惑的包装函数。
$( function () { // ... }); |
尽管后者代码有些难读,上面的两个代码片段功能完全一样。不相信?看下面的jQuery源代码。
// HANDLE: $(function) // Shortcut for document ready if (jQuery.isFunction( selector )){ return rootjQuery.ready( selector ); } |
rootjQuery仅是对jQuery(document)的引用。但你向jQuery函数传递选择器时,它将判断你选择的类型: string, tag, id, 函数等等。如果传递的是函数,jQuery将会调用他的ready()方法,传递匿名函数作为选择器。
5. 保证代码安全
如果开发的代码要发布,对可能的命名冲突提供修补方式很重要。如果在引入你的代码后,有些脚本也有个$函数怎么办?糟糕!答案就是要么调用jQuery的noConflict()方法,或者将你的代码放入一个自调用的匿名函数中,并且把jQuery传递进去。
方法1: NoConflict
var j = jQuery.noConflict(); // 现在,我们用j而不是$ j( '#someDiv' ).hide(); |
// 下面的代码会引用其他库的$函数。
$(‘someDiv’).style.display = ‘none’;
小心这种方法,当发布你的代码时尽量不要用它。这种方法可能使用户迷惑。
方法2: 传递jQuery对象
( function ($) { // 在这个函数中, $总是引用jQuery对象 })(jQuery); |
在底部最后的一组括号自动调用函数–function(){}().然而当调用方法时,我们传递入jQuery对象,它通过$来表示。
方法3: 通过Ready方法传递$
jQuery(document).ready( function ($) { // $ 引用jQuery对象 }); // $ 要么是未定义, 要么是引用其他库的函数 |
6. 足够精明
记住–jQuery仅是JavaScript,不要指望他为你编写的糟糕代码买单。
这意味着,就像我们必须优化JavaScript的for循环一样,jQuery的each方法也是。为什么不这样呢?each仅是一个辅助方法,它在底层仍然使用for语句。
// jQuery中each方法的源代码 each: function ( object, callback, args ){ var name, i = 0; length = object.length, isObj = length === undefined || jQuery.isFunction(object); if (args){ if (isObj){ for (name in object){ if (callback.apply(object[name], args) === false ) { break ; } } } else { for ( ; i < length; ){ for ( callback.apply( object[i++), args) === false ) { break ; } } } // A special, fast, case for the most common use of each } else { if (isObj) { for (name in object){ if (callback.call(object[name], name, object[name]) === false ){ break ; } } } else { for ( var value = object[0];i < length && callback.call(value, i, value) !=== false ; value = object[i++]){} } } return object; } // kick it all off here $(document).ready(UTIL.loadEvents); |
糟糕的
someDivs.each( function () { $( '#anotherDiv' )[0].innerHTML += $( this ).text(); }); <ol> <li>在每一次迭代中查找anotherDiv</li> <li>使用innerHTML属性两次</li> <li>创建一个新的jQuery对象,仅是为了得到文本元素</li> </ol> <h4>更好的</h4> <span class= "label" >JavaScript</span> var someDivs = $( '#container' ).find( '.someDivs' ), contents = []; someDivs.each( function () { contents.push( this .innerHTML); }); $( '#anotherDiv' ).html(contents.join( '' )); |
这样,在each(for)方法中,唯一的工作就是向数组中增加新元素…与查询DOM,获取两次innerHTML属性不同。
这一技巧主要是基于JavaScript的,而不是只针对jQuery。关键是记住jQuery不会为你糟糕的代码补偿。
还有一个情况属于我们正在讨论的情况,就是使用文档片段。
var someUls = $( '#container' ).find( '.someUls' ), frag = document.createDocumentFragment(), li; someUls.each( function (){ li = document.createElement( 'li' ); li.appendChild(document.createTextNode( this .innerHTML)); frag.appendChild(li); }); $( '#anotherUl' )[0].appendChild(frag); |
这里的关键是有多种方式可以完成这样简单的工作,根据浏览器的不同他们都有自身性能的优势。随着你使用jQuery学习JavaScript,你也许会发现你更愿意使用JavaScript的本地属性和方法。如果真是这样,那太好了!
jQuery提供了一层非常出色的抽象层,你应该充分利用,但这并不意味着你必须使用这些方法。例如,在上面的示例中,我们使用each方法。如果你想用for或者while语句替换,完全没问题。
“已经说过了,记住jQuery团队对库做了充分的优化。争论jQuery的each方法和原生的for语句谁优谁劣是愚蠢而没意义的。如果你在项目中使用jQuery,那么时下时间用它的帮助方法,这也是它们存在的意义!”
7. Ajax方法
如果你刚开始深入了解jQuery,有多种Ajax方法可能是你有些迷惑;尽管他们不不必这样。事实上,他们多数都是辅助方法,最终还是调用$.ajax。
- get
- getJSON
- post
- ajax
最为示例,让我们看看getJSON,他让我们获取JSON。
$.getJSON( 'path/to/json' , function (results) { // callback // 结果包含返回的data对象 }); |
在幕后,这个方法首先调用$.get。
getJSON: function (url, data, callback) { return jQuery.get(url, data, callback, "json" ); } |
然后$.get编译传进来的data,再一次,调用”主导”的$.ajax方法。
get: function ( url, data, callback, type ) { // shift arguments if data argument was omited if ( jQuery.isFunction( data ) ) { type = type || callback; callback = data; data = null ; } return jQuery.ajax({ type: "GET" , url: url, data: data, success: callback, dataType: type }); } |
最终,$.ajax做了大量的工作使我们能在所有浏览器中完成异步请求!
“这意味着你可以直接使用$.ajax方法所有的AJAX请求都用它。其他的方法仅仅是帮助方法最终都会使用$.ajax。所以,如果你愿意,跳过中间的过程。这不是什么大不了的事。”
8. 访问原生属性和方法
没错你学了一些JavaScript,已经学过,如锚点标记,你会直接访问属性值:
var anchor = document.getElementById( 'someAnchor' ); //anchor.id // anchor.href // anchor.title // .etc |
唯一的问题是当你使用jQuery来引用DOM元素时这样做不行,不是吗?当然不是。
不会工作
var id = $( '#someAnchor' ).id; |
所以,如果你需要访问href属性(或其他原生属性或方法),你有如下一些选择。
// OPTION 1 - Use jQuery var id = $( '#someAnchor' ).attr( 'id' ); // OPTION 2 - Access the DOM element var id = $( '#someAnchor' )[0].id; // OPTION 3 - Use jQuery's get method var id = $(' #someAnchor').get(0).id; // OPTION 3b - Don't pass an index to get anchorsArray = $( '.someAnchors' ).get(); var thirdId = anchorsArray[2].id; |
“get方法相当有用,因为他可以把jQuery集合转换为数组。”
9. 使PHP检查AJAX请求
当然,我们项目中的绝大多数不能仅依赖JavaScript处理如验证或AJAX请求。当JavaScript被关闭后怎么办?真因为这一原因,一个简单的技巧是用你的服务器端请求来检测AJAX请求。通过设置$.ajax方法的请求头,jQuery是检测非常容易。
// Set header so the called script knows that it's an XMLHttpRequest // Only send the header if it's not a remote XHR if ( !remote ) { xhr.setRequestHeader( "X-Requested-With" , "XMLHttpRequest" ); } |
设置了请求头设置后,现在我们可以用PHP(或者其他语言)来检测这一请求头,然后做相应的处理。对于这一示例,我们可以检测$_SERVER['HTTP_X_REQUESTED_WITH']的值。辅助方法
function isXhr() { return $_SERVER[ 'HTTP_X_REQUESTED_WITH' ] === 'XMLHttpRequest' ; } |
10. jQuery和$
曾经想过为什么或者你怎么就可以交替使用jQuery和$的吗?为了得到答案,查看jQuery的源代码,到最下面。这里你会看到:
window.jQuery = window.$ = jQuery; |
当然,所有jQuery脚本被包装在一个自执行的函数中,这样可以最大限度的控制全局变量的数量。然而这样意味着,jQuery对象在匿名函数外是不可用的。为了修复这点,jQuery对象被暴露给全局的window对象,并且在这一过程中,一个别名$被创建了。
11. 条件性加载jQuery
HTML5 Boilerplate提供了一简洁技巧,当你用的CDN不工作的时候,将会加载 本地版本的jQuery。
<!-- Grab Google CDN jQuery. fall back to local if necessary --> < script >!window.jQuery && document.write('< script src = "js/jquery-1.4.2.min.js" ><\/script>')</ script > |
“解读”上面的代码:如果window.jQuery未定义,一定是从CDN上下载脚本时出了问题。如果是这样,继续&&操作符右侧的部分,插入引用本地jQuery的代码。
12. jQuery Filters
jQuery.expr[':'] is simply an alias for jQuery.expr.filters.
13. A Single Hover Function
对于jQuery1.4, 我们可以只对hover方法传递一个函数。之前,进入和出来的方法都需要传递。
之前
$( '#someElement' ).hover( function () { // mouseover }, function() { // mouseout }); |
现在
$( '#someElement' ).hover( function () { // the toggle() method can be used here,if applicable }); |
注意这不仅是新旧的问题。多数情况下,你还是需要对hover传递两个函数,这非常容易理解。然而,如果你仅需要切换一些元素的状态(或者类似的内容),传递单一的一个匿名函数将会节省很多字节。
传递属性对象
对于jQuery 1.4, 我们现在可以给jQuery函数传递一个对象作为第二个参数。
之前
$( '<a>' ) .attr({ id : 'someId' , className : 'someClass' , href : 'somePath.html' }); </a> |
现在
$( '</a>' , { id : 'someId' , className : 'someClass' , href : 'somePath.html' }); |
这种写法不仅节省了字节,还有带来了更清晰的代码。除了元素属性,我们甚至可以传递jQuery特有的属性和事件,如click或text.