惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Simon Willison's Weblog
Simon Willison's Weblog
P
Privacy International News Feed
www.infosecurity-magazine.com
www.infosecurity-magazine.com
T
Troy Hunt's Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
Attack and Defense Labs
Attack and Defense Labs
S
Secure Thoughts
V2EX - 技术
V2EX - 技术
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
O
OpenAI News
Cloudbric
Cloudbric
Google Online Security Blog
Google Online Security Blog
Schneier on Security
Schneier on Security
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Help Net Security
Help Net Security
Cyberwarzone
Cyberwarzone
G
GRAHAM CLULEY
L
Lohrmann on Cybersecurity
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Spread Privacy
Spread Privacy
NISL@THU
NISL@THU
N
News and Events Feed by Topic
T
Tenable Blog
S
Security @ Cisco Blogs
N
News and Events Feed by Topic
The Hacker News
The Hacker News
C
CXSECURITY Database RSS Feed - CXSecurity.com
宝玉的分享
宝玉的分享
月光博客
月光博客
酷 壳 – CoolShell
酷 壳 – CoolShell
美团技术团队
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google DeepMind News
Google DeepMind News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tailwind CSS Blog
V
Visual Studio Blog
P
Proofpoint News Feed
Webroot Blog
Webroot Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 三生石上(FineUI控件)
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Jina AI
Jina AI
雷峰网
雷峰网
T
The Blog of Author Tim Ferriss
Hugging Face - Blog
Hugging Face - Blog
腾讯CDC
L
LangChain Blog
The Register - Security
The Register - Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 聂微东

博客园 - miao~

C# 截取1位小数 ASP.NET 获取MSN联系人列表 扩展老赵的 直接通过User Control生成HTML 分页功能 小技巧在Repeater控件中找到对应模板内的控件 大型互联网网站架构心得之一:分 UML 序列图 sql存储过程教程 Create an ASP.NET AJAX Style Folder Explorer BS程序代码与安全与基本攻击/防御模式 利用 .NET 3.5 的 Syndication 相关类生成 RSS RSS2.0规范 利用 .NET 3.5 的 Syndication 相关类读取 RSS Feeds 使用linq to xml 快速创建自己的Rss 代码设计简单规范 VS.PHP 在Visual Studio 下的 PHP 开发 IDE 工具 开发一个适合Ajax+JSON+jQuery环境使用的多功能页码栏——jPagerBar-1.1.1 最实用的12条css技巧 不错的下拉列表.HTML控件和服务器控件都可以使用 两个粒度看Asp.net生命周期
窥探jQuery——面向JavaScript程序员
miao~ · 2008-04-03 · via 博客园 - miao~

jQuery 在2006年1月现身时,给我的第一印象,是这玩意儿构造得很精明。基于CSS选择器(CSS selectors)来打点一切,其思路相当灵巧(参考getElementsBySelector)。但链盒工事(chaining stuff)看起来更像个噱头,并且整体看来,jQuery库提供的功能并不能覆盖所有基础性的东西。因此我断定,jQuery只会昙花一现。

几个月以来,我逐渐明白自己想错了。从技术工艺上考量,jQuery十分凌厉。它用简洁的方法,把大量常用功能封装起来,并提供精巧的插入式API,来满足标准库之外的功能模块的实现。jQuery秉持的核心,乃DOM元素的集合(译注:通常是某些子集合)——它把元素集合作为一个根本,给高度抽象出来了。最重要的,是这种遵循最佳实践的抽象,能让jQuery与其他JavaScript代码相处融洽。

很多对jQuery的介绍,都是针对设计师和初级开发人员。接下来我想说明,为什么jQuery也会吸引那些富有经验的开发人员。

名称空间(Namespacing)

编写可重用的、优秀的JavaScript代码,其关键在于对名称空间的积极把控。JavaScript只拥有单一的、全局的名称空间(即window对象),而很多程序员(以及一些库)恣意地为之添加各种东西。要知道

全局变量是魔鬼!聪明的开发人员,会使用类似组件模式的技术,来尽力减少全局对象的数量。

jQuery仅向全局名称空间引入一个标记:jQuery函数/对象。其余的要么是jQuery的直接属性(译注:原文‘directy property’系笔误,应是‘direct property’),要么就是调用jQuery函数所返回的对象的方法。

那“语言升级”(language enhancements)又是什么呢?大多数库会提供映射,过滤,剥离,往往是浏览器的JavaScript引擎所缺少的那些功能。还有一些库,直接扩展了JavaScript内置的String和Array类,但这是冒险的做法。String.prototype和Array.prototype也有各自的名称空间,在其内添加的属性一旦发生冲突,所带来的风险,不亚于在全局环境下的草率大意。

在语言升级方面,jQuery提供了很多函数(功能),但每个函数都被赋给jQuery对象的属性:jQuery.each,jQuery.extend,jQuery.grep,jQuery.map,jQuery.merge以及jQuery.trim。如此一来,它们就不会跟其他代码产生冲突。

声名狼藉的$函数(The infamous $ function)

刚才我说到,jQuery是唯一被引入的全局标记,其实并不尽然:$标记作为jQuery的快捷方式,也被引入进来。庆幸的是,$的存在不会带来负面影响:如果你需要让原始的$起死回生(比如,这之前你的代码使用了Prototype),你可以调用jQuery.noConflict()来恢复它。

如果你既想拥有$的便利,又不希望jQuery跟其他同样使用了全局$函数的代码发生冲突,可遵循jQuery文档所建议的惯用方式:

(function($) {
// 在这个函数体里,$可作为jQuery的引用
// 很方便,对吧?
})(jQuery);

把一切都附加到$标记的做法,曾让我认为jQuery华而不实。不过,从体系的角度来审视这种设计,一切又是非常明了的——尽管我常喜欢在代码中定义自己的$快捷方式。

选取元素(Selecting some elements)

jQuery的每个操作,都以选取DOM中一个或更多的节点(nodes)作为开始。jQuery(拥有一种真正的面向特定领域)的

选取语法,是十分有趣的,它结合了CSS 1,CSS 2,部分CSS 3语法,一些XPath语法,以及一些特定的扩展。在这里我不会做详细介绍,我只列出几个有用的例子:

jQuery('div.panel')
选取了所有class="panel"的div
jQuery('p#intro')
选取了所有id="intro"的段落
jQuery('div#content a:visible')
选取了id="content"的div中所有可见的链接
jQuery('input[@name=email]')
选取了所有name="email"的输入域
jQuery('table.orders tr:odd')
选取了类名为“orders”的表中所有的奇数行
jQuery('a[@href^="http://"]')
选取了所有(以http://开头的)外部链接
jQuery('p[a]')
选取了所有包含一个或多个链接的段落

上述例子中,:visible和:odd是jQuery实现的扩展,很具特色。而属性的选取使用@作为标记,其方式和XPath一样,要优于CSS 2。

jQuery的这套选取语法包罗万象,有些类似正则表达式,想完全消化是需要花上一段时间的。

把玩一下(Doing stuff with them)

通过jQuery的选取操作,我们能得到一些很棒的“素材”(beast)。它们是一个集合,包含了DOM元素,并且类似数组那样,拥有length属性;通过索引可以访问集合中的元素。在

Firebug console的交互模式下,集合也被显示成一个数组,这个特性非常有用。集合实际上是一个jQuery对象,这个对象被赋予了很多方法(methods),用来查询,修改,扩展集合中的元素。

jQuery的方法(methods),本质上可分成三种:一种可以操作那些符合匹配的元素;一种可以返回第一个匹配到的对象的值;一种可以变更被选取的集合。

我不会列出所有的方法(可参考

visualjquery.com),但我用例子做一下说明。如果你的浏览器装了Firebug,你可以以交互方式运行这些示例代码:首先使用这个bookmarklet(译注[1])把jQuery库载入至浏览器的任意页面,然后把示例代码粘贴到Firebug console中。

jQuery('div#primary').width(300);
把id="primary"的div的宽度设为300px
jQuery('p').css('line-height', '1.8em');
把所有段落的line-height设为1.8em
jQuery('li:odd').css({color: 'white', backgroundColor: 'black'});
向间隔的list项添加两个CSS规则;注意css()函数可以用一个对象来代替两个字符串作为参数
jQuery('a[@href^="http://"]').addClass('external').attr('target', '_blank');
向所有(以http://开头的)外部链接添加“external”类,然后策略性地加上target="_blank"属性。这个示例用到了链盒(chaining),稍后会做介绍。
jQuery('blockquote').each(function(el) { alert(jQuery(this).text()) });
遍历页面上的每个<blockquote>,并显示出它的文字内容(包括HTML标签)
jQuery('a').html('Click here!');
用阴险的“Click here!”代替页面上所有的链接<a>的文字

下面的示例展示了jQuery如何取得第一个匹配到的对象的值:

var width = jQuery('div').width();
页面上第一个div的宽度
var src = jQuery('img').attr('src');
页面上第一张图片的src属性值
var color = jQuery('h1').css('color');
第一个<h1>的颜色样式值

在jQuery 的方法构造中,蕴含着令人惬意的对称性:当向方法传递两个参数或一个对象时,方法可被用来执行设置操作;如果只向方法传递一个参数,则可以让它执行取值操作(译注:读者可对照上面的示例代码感受一下)。这种对称性设计贯穿了jQuery体系,使得API的文法更容易被记忆。

本节最后的例子,展示了一些可变更被选取的元素集合的方法。这些方法大多都提高了检索DOM的简易程度:

jQuery('div').not('[@id]')
返回那些没有id属性的div
jQuery('h2').parent()
返回那些是<h2>的直接父节点元素
jQuery('blockquote').children()
返回所有<blockquote>的子节点元素
jQuery('p').eq(4).next()
在页面上找到第五个段落(译注:因为集合的元素索引从0开始),然后根据节点的树层结构关系,找到并返回这个段落节点右侧的兄弟节点元素
jQuery('input:text:first').parents('form')
找到并返回页面上第一个type="text"的输入域所在的form节点元素,parents()的可选参数是另一个选择器

链盒(Chaining)

jQuery 开发团队经常夸耀jQuery的链盒理念(译注[2]),甚至在网站首页上宣扬“jQuery将改变你编写JavaScript的方式”。我个人感觉,这么做多少有点误导大众,我愿意告诉大家,你完全可以取jQuery之长,却应避免冗长的方法链盒(chains of methods)。

也就是说,链盒有时会像变戏法一样。除了使用链盒将各种操作DOM的方法粘到一起,你也可以使用jQuery的end()方法,来实现在特定范围内推进或回溯你需要得到的元素。这个概念很难解释清楚。本质上讲,每次使用(诸如children()或filter())方法来改变元素集合时,你可以在这些方法之后使用end(),来重新定位你最初选取的元素集合。关于这点,Jesse Skinner在他的

Simplify Ajax development with jQuery(译注[3])教程中给出了实例:

$('form#login')
// 第一步,隐藏表单中那些带有'optional'类的<label>
.find('label.optional').hide().end()
// 第二步,为表单的密码输入域渲染上红色边框
.find('input:password').css('border', '1px solid red').end()
// 第三步,为表单加上提交处理
.submit(function(){
return confirm('Are you sure you want to submit?');
});

这个示例读起来就像句俏皮话。整个过程是,先选取一个表单,再在其中选取一些元素做修改,然后回溯到表单,为它定义一个submit()处理。

示例很酷,但如果你不习惯,也可以不这么用。我就很乐意用自定义变量来规划代码。

操作DOM(DOM Manipulation)

jQuery提供了几个大规模操作DOM的卓越方法。第一种非常让人惊叹:jQuery()函数能把HTML片段插入DOM元素中(实际上,函数会留意以'<'打头的字符串参数):

var div = jQuery('<div>Some text</div>');

一旦你创建好了div,便可以继续用链盒向其添加属性:

var div = jQuery('<div>Some text</div>').addClass('inserted').attr('id', 'foo');

现在把div加到body上:

div.appendTo(document.body)

或用选择器把div加到已知元素的前面:

div.prependTo('div#primary')

处理事件(Handling events)

任何JavaScript库都需要事件处理能力,jQuery也不例外。类似attr()和css()的行为,各种与事件处理相关的方法也有双重用途:一种是把函数当作参数,赋给事件处理器;一种是不带参数,可以模拟事件被触发(译注:前提是事件已经定义,可参考visualjquery.com > Events > click()):

jQuery('p').click(function() { jQuery(this).css('background-color', 'red'); });
为所有段落增加点击事件,当你点击它们时,段落背景会变成红色
jQuery('p:first').click()
然后在第一个段落上模拟点击的动作,它的背景会变成红色

类似的函数还包括mouseover,keyup等,对应着浏览器通常支持的那些动作。留意一下事件处理中的'this'关键字,它代表触发事件的元素;jQuery(this)是一种惯用语法,可以让this所代表的元素应用各种jQuery方法。

这里有两个与事件相关的函数值得仔细说一下:

jQuery('a').hover(function() {
jQuery(this).css('background-color', 'orange');
}, function() {
jQuery(this).css('background-color', 'white');
});

hover()可设定两个函数,分别对应onmouseover和onmouseout事件。

jQuery('p').one('click', function() { alert(jQuery(this).html()); });

one()设定的事件在第一次被触发后便被移除。上面的示例会让所有段落在第一次被点击时显示其文字内容。

凭借bind()和trigger()方法,jQuery也可以支持自定义事件(click()家族仅仅是便捷方法,只支持有限的事件)。自定义事件可接受参数,trigger()可接受数组作为参数,来做各种处理操作:

jQuery(document).bind('stuffHappened', function(event, msg) {
alert('stuff happened: ' + msg);
});
jQuery(document).trigger('stuffHappened', ['Hello!']);

渐进式编码Unobtrusive scripting

本小节的标题很令我钟意。我一直认为,最好的Web应用程序,往往是那些在脚本被禁用后仍能正常使用的程序。想建立这样的应用程序,最好的方法就是遵循渐进式编码,让普通页面完全加载后,再为页面中的元素赋以事件处理(更多信息可参考

渐进式编码Hijax)。

jQuery对这种编码策略提供了绝好支持。首先,从整体上看,节点选取暗合jQuery以及渐进式编码的核心理念。其次,针对

window.onload问题,jQuery提供了一套解决方案,这套方案借鉴了Dean Edward的成果,使得以“DOM加载完毕”为信号的事件能跨浏览器工作。你可以在浏览器完全加载DOM后设定并运行一个函数,如下所示:

jQuery(document).ready(function() {
alert('The DOM is ready!');
});

你甚至可以直接传递一个函数给jQuery(),以更简洁的方式达到同样效果:

jQuery(function() {
alert('The DOM is ready!');
});

jQuery与Ajax(jQuery and Ajax)

在我所知道的主流JavaScript库中,jQuery拥有最棒的Ajax API。最简单的Ajax调用如:

jQuery('div#intro').load('/some/fragment.html');

代码以GET请求方式,从/some/fragment.html文件中获取HTML片段,并把片段装载到id="intro"的div中。

当我第一次看到这行代码时,几乎对它没什么印象。这看起来非常简洁,但如果你想用jQuery做些更复杂的事情,比如显示Ajax装载进度,该如何做呢? jQuery为你准备了一些可自定义的事件(ajaxStart,ajaxComplete,ajaxError等等),来实现你想要的代码。同时 jQuery也提供了广泛的底层API,来实现更复杂的Ajax交互:

jQuery.get('/some/script.php', {'name': 'Simon'}, function(data) {
alert('The server said: ' + data);
}); // 以GET方式通过/some/script.php?name=Simon获取数据

jQuery.post('/some/script.php', {'name': 'Simon'}, function(data) {
alert('The server said: ' + data);
}); // 以POST方式向/some/script.php发送请求

jQuery.getJSON('/some.json', function(json) {
alert('JSON rocks: ' + json.foo + ' ' + json.bar);
}); // 从/some.json接收并解析数据,把数据转换成JSON格式

jQuery.getScript('/script.js'); // 以GET方式获取/script.js脚本并用eval()执行

插件(Plugins)

就你所能获得的功能的数量而言,jQuery库其实是相当小的——对代码做紧凑处理后只有20KB左右,甚至用gzip压缩后会变得更小。向标准库添加额外功能时,需用插件的方式来做,它可以(也确实能够)向现有的jQuery实例对象添加全新的方法。如果你想执行:

jQuery('p').bounceAroundTheScreenAndTurnGreen();

jQuery的插件机制提供了文档说明型的挂载方式(documented hooks),可以实现把上述方法添加到jQuery中。这种简易的创建形式,吸引了很多插件作者,他们让人印象深刻;现在插件目录中已经有超过100个插件了。

真正绝妙的,是你可以像自定义方法那样,来定义选择器。比如,

moreSelectors插件实现了诸如“div:color(red)”的方法,来匹配包含红色文本的div。

并非天衣无缝Leaky abstractions

在发掘jQuery各种特性的同时,我也被某个我视之为教条(philosophical blocker)的东西所折磨着。几年来,我总是建议大家使用一种JavaScript库,前提是你们愿意梳理它的源码,并把它的工作原理彻底搞懂。我发出如此论调,是基于Joel Spolsky的

不健全抽象的法则(译注[4])。在那篇文章中,Joel指出,API把复杂性隐藏的越多,当它出现无法应付的意外时,你越有可能遭遇更多的麻烦。浏览器平台是不健全抽象的最佳代表,所以当库无法帮你摆脱困境时,你要自寻解药。保持警觉非常重要。

jQuery 使用了相当不可思议的技术,以求实现它所设想的各种功能——其中一些(比如选择器的代码)真是震天骇地。如果有必要彻底搞懂一个库的工作原理,那么对大多数开发人员来说,jQuery不会是上佳之选。然而,jQuery拥有极高的人气,并且没有太多与之相关的恐怖经典流于街巷(译注:原文是a distinct lack of horror stories,比如微软Win95的“蓝屏”就是恐怖经典:),所以具体到jQuery所用技艺的邪正之分,也就变得不那么重要了。

我想,我必须重新审视曾给大家的建议。库的运作机制并不是问题焦点:关键是应看清更具普遍性的潜在问题,知晓浏览器之间的差别,以及你使用库的哪种技术,来消除差别造成的负面影响。没有哪种库可以一劳永逸地帮你克服浏览器的古怪行为。但只要你对应付潜在问题训练有素,便可把握经脉,指出问题的源头——无论它们来自你自己编写的代码,还是库或者应付策略本身。

结语(To conclude)

我费了那么多口舌,希望能让大家明白,jQuery不只是又一个JavaScript库那么简单——它蕴含了很多值得品味揣摩的理念,甚至能启迪那些骨灰级的JavaScript程序员。如果你不打算尝试jQuery,但仍值得去花些时间探索一下jQuery的生态体系(the jQuery ecosystem)。

Simon Willison写于2007年8月15日 凌晨2:27

译注:

[1] bookmarklet在原文中指的是一段“Insert jQuery”的JavaScript代码,由于译者使用Google Docs进行在线翻译,链接中的JS代码被编辑器屏蔽掉了,下面列出的代码可粘贴到浏览器的地址栏中执行,执行后才可以继续用示例代码查看jQuery的选取效果:
javascript:void(function(){var s=document.createElement('script');s.src='http://code.jquery.com/jquery-1.1.2.js';document.getElementsByTagName('head')[0].appendChild(s);}())

[2] 本文使用的术语“链盒”,大抵可对应单词chain/chaining/chainable;译者在参考

jQuery Magazine issue 1对jQuery 选择器运行方式的图解后,确定了这种译法。汉字是象形文字,按“盒”字的构造来体会jQuery颇有意趣:比如上面的“人”字,不正是选择器“吐出”特定元素的“嘴”吗?而“人”字下面“一”“口”“皿”的逐层累积,其形状又类似jQuery的Logo,并让人联想到链式选取过程中不同的元素集合;“盒” 即是“桶”(bucket),译者自以为绝妙!

[3] IBM developerWorks中国的官方翻译版本《

使用jQuery简化Ajax开发》;译言版本分12两部分,由令狐葱翻译。