锻造车间 - 编写 jQuery 插件
过去两个月,本专栏探讨了用于 JavaScript 编程的 jQuery 库。jQuery 是近年来涌现的几个流行的库之一(如 Prototype、YUI 和 Dojo),它使得以更令人满意和响应迅速的方式使用 JavaScript 成为可能,通过融入类似桌面的行为。
jQuery 受欢迎的部分原因是其庞大的插件库。几乎可以找到任何你能想到的功能的插件——从 GUI 小部件到导航辅助工具到文本转换。插件使得隔离和重用某些行为成为可能,从而实现 Ruby 世界中称为 DRY(不要重复自己)的目标。
正如我上个月展示的那样,使用插件通常非常容易。下载插件;安装随附的任何 CSS 和 JavaScript 文件,然后使用标准的 <script> 标签将 JavaScript 文件合并到您网站的一个或多个 HTML 页面中。最后,使用 jQuery 的事件处理函数,通常插入到 $(document).ready 中,将插件附加到一个或多个页面元素。
如果您使用 jQuery,并且发现自己一遍又一遍地重复相同的 JavaScript 模式,您可能需要考虑编写自己的插件。是否将该插件分发给 jQuery 社区的其他成员取决于许多因素,但通过将其制作为插件,您可以使您的所有应用程序都以类似的方式加载和使用该库。
jQuery 插件是 JavaScript 代码的打包机制。这意味着要创建您的插件,您首先必须有一些需要打包的 JavaScript 代码。
因此,作为本月的示例,我决定创建一个简单的 Ubbi Dubbi 翻译器。Ubbi Dubbi,正如你们中的一些人可能知道的那样,是儿童的“秘密”语言,在 1970 年代(我观看时)由美国公共电视台节目 Zoom 推广,然后在 1990 年代再次流行。Ubbi Dubbi 的规则很简单。每个元音(a、e、i、o 和 u)都以字母 ub 为前缀。所以,hello 变成 hubellubo。自学说 Ubbi Dubbi 并不难,而且听起来很滑稽。试试看!
无论如何,让我们首先创建一个基本的 JavaScript 程序,使用 jQuery,当鼠标光标悬停在文本上时,将文本转换为 Ubbi Dubbi。让我们从一个简单的 HTML 文件 ubbi.html(列表 1)开始。正如您所见,此文件中没有 JavaScript 代码。相反,我们将使用 jQuery 鼓励的“非侵入式”风格,将我们的 JavaScript 代码编写在一个单独的文件(ubbi.js,列表 2)中,然后我们通过 <script> 标签包含该文件。
列表 1. ubbi.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript" src="ubbi.js"></script> <link rel="stylesheet" type="text/css" media="screen" href="ubbi.css" /> <title>Ubbi Dubbi</title> </head> <body> <h1>Ubbi Dubbi</h1> <p class="ubbi">This is in Ubbi Dubbi.</p> <p class="ubbi"> Today, we will learn how to make cereal. First, pour the cereal into a bowl. Then pour milk onto the cereal. Finally, eat the cereal with a spoon. Delicious! </p> </body> </html>
列表 2. ubbi.js
function ubbify(text) { return text.replace(/([aeiou])/gi, 'ub$1'); } $(document).ready(function() { $(".ubbi").bind('mouseover', function() { var original_text = $(this).html(); $(this).attr({originalText: original_text}); $(this).html(ubbify(original_text)); }); $(".ubbi").bind('mouseout', function() { $(this).html($(this).attr("originalText")); $(this).attr({originalText: ""}); }); });
HTML 本身并不令人惊讶或兴奋。我们有两个文本段落,每个段落都分配了类 ubbi。在 JavaScript 文件中,我们使用 .ubbi 选择器为 mouseover 和 mouseout 事件设置处理程序。这就是奇迹真正发生的地方。当鼠标悬停在指定的段落上时,文本被转换为 Ubbi Dubbi。当鼠标移开时,文本恢复为其原始形式。
翻译取决于我们的 ubbify 函数,其定义如下
function ubbify(text) { return text.replace(/([aeiou])/gi, 'ub$1'); }
上面的 JavaScript 函数接受一个文本参数。它将任何元音替换为字符串 ub,后跟被替换的字母。诚然,这里有一个与以元音开头的大写单词相关的错误。修复该错误留给读者作为练习。
我们的 mouseover 处理程序定义如下
$(".ubbi").bind('mouseover', function() { var original_text = $(this).html(); $(this).attr({originalText: original_text}); $(this).html(ubbify(original_text)); });
这通过使用 jQuery 的 bind 函数来实现,当特定事件在 HTML 元素(或元素集合)上触发时,该函数会调用一个函数。因此,在这种特定情况下,我们告诉 JavaScript,每个类为 ubbi 的 HTML 元素都应该在鼠标光标悬停在其上时调用我们的函数。该函数本身抓取原始文本,将其放入名为 originalText 的属性中,然后用 ubbified 文本替换原始文本。
mouseout 处理程序类似,执行大致相反的操作,但没有 ubbification
$(".ubbi").bind('mouseout', function() { $(this).html($(this).attr("originalText")); $(this).attr({originalText: ""}); });
为了增加一些活力和样式,我们还有 ubbi.css,它使用 .ubbi:hover 伪选择器在鼠标悬停在文本上时对其进行着色和斜体化(列表 3)。
列表 3. ubbi.css
.ubbi:hover { font-style: italic; border: 0.5px dashed #000; background-color: #cc9999; }
CSS 和 JavaScript 的结合既有趣又有点令人兴奋。通常,文本看起来符合您的预期。但是,当您将鼠标移到一段文本上时,它会转换为 Ubbi Dubbi。Prubetty cubo-ubol,rubight?(相当酷,对吧?)
这段 JavaScript 代码工作得很好。但是,可能普遍需要 Ubbi Dubbi 翻译器,当鼠标悬停在文本上时激活。如果有人可以简单地使文档中的每个段落都自动 Ubbify,那就太好了,通过
$(document).ready(function() { $("p").ubbify(); });
为了做到这一点,让我们创建一个 jQuery 插件。插件在合并后,将向 jQuery 对象添加一个新函数。这意味着,我们的 ubbify 函数不再位于全局命名空间中,也不再从事件处理程序中调用,我们将在 jQuery 命名空间中定义一个函数,它将由也在该命名空间中定义的处理程序调用。
为了实现这一点,我们需要稍微重组一下。首先,我们需要重命名我们的 JavaScript 文件,因为每个插件都需要采用 jquery.PLUGIN.js 格式。在这种情况下,我将其命名为 jquery.ubbi.js。
接下来,我们需要定义我们的 ubbify 函数,以便全局 jQuery 对象能够识别它。为此,我们在 jQuery 命名空间内定义 ubbify
$.fn.ubbify = function () { // implementation goes here }
等一下——我们正在定义的 $.fn 是什么?事实证明,如果我们想为 jQuery 对象定义一个全局方法,通常别名为 $,我们必须将该函数分配给 $.fn 对象。
但是,再等一下——可以重新定义 $,使其不再是 $ 函数的别名。这使得 jQuery 可以与 Prototype 等 JavaScript 库很好地配合使用,Prototype 也使用 $,但方式非常不同。因此,许多 jQuery 插件教程告诉您不要使用 $,而是使用完整的 jQuery 对象,如下所示
jQuery.fn.ubbify = function () { // implementation goes here }
另一种解决方案是将整个函数定义包装在闭包(即具有状态的函数)中,将闭包作为具有变量绑定的环境提供给 jQuery 对象
($.fn.ubbify = function () { // implementation goes here });
现在我们已经解决了这个问题,我们可以在其新的插件主目录中定义我们的函数。列表 4 包含 jquery.ubbi.js,这是一个 jQuery 插件,它执行我们之前所做的所有操作,但在插件的上下文中。
列表 4. jquery.ubbi.js
(function($) { $.fn.ubbi = function(options) { // Private function function ubbify(text) { return text.replace(/([aeiou])/gi, 'ub$1'); } // Return the results of iterating over our inputs return this.each( function() { $(this).bind( 'mouseover', function() { var original_text = $(this).html(); $(this).attr({originalText: original_text}); $(this).html(ubbify(original_text)); }); $(this).bind( 'mouseout', function() { $(this).html( $(this).attr("originalText")); $(this).attr({originalText: ""}); }); }); }; })(jQuery);
关于 jQuery 最有趣的事情之一是,由于 CSS 选择器的存在,它可以接受任意数量的参数。可以为单个段落调用一个函数,通过 DOM ID 标识。或者,它可以在许多标签上调用,或者在具有特定类的标签上调用。我们的函数需要处理所有或任何这些情况,并且当它完成时,我们的函数必须返回 jQuery 对象,以便可以将其使用“链接”到另一组指令。
我们通过迭代每个参数并返回结果来做到这一点,如下所示
return this.each( function() { ... });
jQuery 将 .each 定义为迭代器,它对调用它的对象的每个元素进行操作。在这种情况下,我们获取每个提交的元素并将它们传递给一个函数。当然,该函数分配事件处理程序 mouseover 和 mouseout。请注意,这些函数现在是如何在 $(this) 上调用的,$(this) 是当前元素的 jQuery 版本。
最后,我们的 ubbify 函数在 $.fn.ubbi 定义中私有定义。我们的 ubbify 函数可供 $.fn.ubbi 的定义中的任何和所有用户使用,诚然,目前函数数量非常少。
在我们的插件就位后,我们所要做的就是告诉我们的 HTML 文件加载插件并以正确的方式调用它
<script type="text/javascript" src="jquery.ubbi.js"></script> <script type="text/javascript"> $(document).ready(function() { $(".ubbi").ubbi(); }); </script>
请注意,必须在加载任何插件之前加载 jquery.js。我们可以使用以下方法将我们的 ubbi 插件应用于页面上的所有段落
$("p").ubbi();
有了我们的 Ubbi 插件(plubugubin?),现在为人们提供 Ubbi Dubbi 翻译变得容易得多。感谢 jQuery 的插件机制,我们可以分发我们的插件供其他人使用,而无需阅读或理解代码。我们修改后的简单 HTML 文件如列表 5 所示。
列表 5. ubbi2.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript" src="jquery.ubbi.js"></script> <script type="text/javascript"> $(document).ready(function() { $(".ubbi").ubbi(); }); </script> <link rel="stylesheet" type="text/css" media="screen" href="ubbi.css" /> <title>Ubbi Dubbi</title> </head> <body> <h1>Ubbi Dubbi</h1> <p class="ubbi">This is not in Ubbi Dubbi.</p> <p class="ubbi"> Today, we will learn how to make cereal. First, pour the cereal into a bowl. Then pour milk onto the cereal. Finally, eat the cereal with a spoon. Delicious! </p> </body> </html>
jQuery 是一个令人惊叹的 JavaScript 库,但其特别令人印象深刻的功能之一是对插件的支持。既然您已经了解了编写插件是多么容易,请尝试思考如何通过为他人发布一个或多个插件来为社区提供价值。
资源
关于 JavaScript 和 jQuery 有很多资源,包括印刷版和在线资源。
在 Packt Press 出版的书籍中,我喜欢 Jonathan Chaffer 和 Karl Sweebber 编写的 Learning jQuery,这本书非常适合已经有其他语言经验(甚至可能是 JavaScript)的 Web 开发人员。它回顾了 JavaScript 程序员可以使用 jQuery 完成的许多不同类型的功能。
David Flanagan 的 JavaScript: The Definitive Guide 仍然是一本优秀的资源,尽管我承认拥有 jQuery 已经大大减少了我需要了解的基础 JavaScript 知识。
同样,有很多博客文章可能会有所帮助,包括:www.learningjquery.com/2007/10/a-plugin-development-pattern、tkramar.blogspot.com/2008/02/improve-your-jquery-fu-write-plugins.html 和 www.bennadel.com/blog/800-My-First-jQuery-Plugin.htm。
Reuven M. Lerner,一位长期的 Web/数据库开发人员和顾问,是西北大学学习科学专业的博士候选人,研究在线学习社区。最近,他在芝加哥地区生活四年后,(与妻子和三个孩子)返回以色列莫迪因的家。