Submitted by gouki on 2009, April 13, 1:19 PM
jQuery这种轻量级的框架大家使用的都比较多,1.3后效率据说又上升了不少,因此,开发基于jQuery的插件也就显得那么理所当然了。
一般来说,jQuery有两种插件模式,一种是:$.extends({}) ,另一种是:$.fn.extends({})
个人理解:前一种是属于全局性的调用,后一种是针对某个元件的绑定调用。这当然是简单的理解。但事实上,也基本上就是这么做的。
官方的例子也比较简单:
JavaScript代码
- $.extend({
- max: function(a, b) {
- return a > b ? a : b;
- },
- min: function(a, b) {
- return a > b ? b : a;
- },
- avg: function(a, b) {
- return a / b;
- }
- });
看上去是不是容易?然后就可以直接调用了。 $.min(2,3) 返回2
一般情况下,为了在扩展中使用$,却又担心的页面使用了jQuery.noConflict()而取消了$指向jQuery的引用,则需要这样写
JavaScript代码
- ( function($){
- $.fn.extend({
-
- });
- })(jQuery);
这样写不仅能够将$显式指向jQuery,而且将产生的变量封在function的作用范围内,不会污染window全局。
而基于$.fn.extends({})的扩展又如何开发呢?
在这里我引用Roby的译文:http://www.robysky.com/archives/171
英文原文:http://www.learningjquery.com/2007/10/a-plugin-development-pattern
我认为以下插件开发模式是必须应该掌握的:
1.在JQuery命名空间内声明一个特定的命名;
2.接收参数来控制插件的行为;
3.提供公有方法访问插件的配置项值;
4.提供公有方法来访问插件中其他的方法(如果可能的话);
5.保证私有方法是私有的;
6.支持元数据插件;
下面,我将逐一讲述上面的内容,并在同时给出相关的简单插件开发代码。
1.在JQuery命名空间内声明一个特定的命名
这意味着开发的是一个单一命名的插件脚本,如果你的脚本包含多个插件或者有补充性质的插件,比如$.fn.doSomething() 和$.fn.undoSomething(),那你得声明多个命名了。但是总体来说,当开发一个插件时,我们应该努力做到用一个单一的命名来搞定整个插件。
在例子中,我们将声明一个名为“hilight”的插件。
JavaScript代码
- $.fn.hilight = function() {
-
- };
我们可以这样调用:
$(’#myDiv’).hilight();
但是假如我们需要打破这种单一的命名和调用方式呢?有很多理由支持我们这么做:设计上的需要;更加简单和可读的配置;而且那样将更加符合OO的要求。
在没有给命名空间来到麻烦的前提下,将插件的部署打破成为多个函数的形式将是十分繁琐的。我们通过认识并利用JavaScript中 functions是最高层的对象,和其他对象一样,functions可以被赋予属性,前面我们已经将hilight命名声明在了JQuery的原型对象上,那么,其实,其他的我们想扩展的属性或对象都能够在hilight上进行声明。稍后将详细讲述此点。
2.接收参数来控制插件的行为;
来为我们的hilight插件添加指定前景和背景色的功能,我们需要在函数中允许一个object类型的选项设置。如下所展示的那样:
JavaScript代码
- $.fn.hilight = function(options) {
- var defaults = {
- foreground: 'red',
- background: 'yellow'
- };
-
- var opts = $.extend(defaults, options);
-
- };
现在,我们的插件可以这样来调用:
$(’#myDiv’).hilight({
foreground: ‘blue’
});
3.提供公有方法访问插件的配置项值;
上面的代码我们可以做一下改进,使得插件的默认值可以在插件之外被设置。这无疑是十分重要的,因为它使得插件用户可以使用最少的代码来修改插件配置,这其实是我们利用函数对象的开始。
JavaScript代码
-
- $.fn.hilight = function(options) {
-
-
-
- var opts = $.extend({}, $.fn.hilight.defaults, options);
-
- };
-
-
- $.fn.hilight.defaults = {
- foreground: 'red',
- background: 'yellow'
- };
4.提供公有方法来访问插件中其他的方法(如果可能的话)
这里要讲的方法和前面的讲解一脉相承,用此方法来扩展你的插件(而且能够让其他人进行扩展)是件很有意思的事情。例如,在扩展hilight插件时,我们可以定义一个format方法用来格式化高亮显示的文本,原来的hilight插件和扩展了format方法的插件代码如下:
JavaScript代码
- $.fn.hilight = function(options) {
-
- return this.each(function() {
- var $this = $(this);
- ...
- var markup = $this.html();
-
- markup = $.fn.hilight.format(markup);
- $this.html(markup);
- });
- };
-
-
- $.fn.hilight.format = function(txt) {'
- return '<strong>' + txt + '</strong>';
- };
如前面所述,我们已经很容易的通过设置options对象的属性来允许一个回调函数来覆写默认的格式设置。在这里有另外一个非常棒的方法来个性化你的插件,上面展示的方法实际上就是通过暴露format方法,使其可以被重新定义。这种做法使得其他人可以采用他们自己的习惯和方式来重写你的插件,这意味着他们可以为你的插件写额外的扩展插件。
仔细考量一下前面我们用到的插件例子程序,你可能会想“我们究竟应该在什么时候使用这种插件方式来实现需求”的问题。一个来自现实应用中的插件便是“ Cycle Plugin”,它是一个支持多种滑动显示特效的插件,特效包括滚动、滑动和渐变等等。但是,实际上,并没有办法来定义每一个可能会用在滑动变幻上的特效。这就是这种扩展方式的有用之处。“ Cycle Plugin”插件暴露了”transitions”对象,这使得用户只需要按照如下方式便可以添加自己的变幻定义:
$.fn.cycle.transitions = {
…
};
这种技巧使得用户可以定义或者采用自己习惯的方式来扩展“ Cycle Plugin”。
5.保证私有方法是私有的;
上面提到的暴露插件中的公有方法的技巧使得插件能够被覆写,这将使插件变得十分灵活而强大,但至于哪一部分,那些属性和方法应该被暴露出来,你得小心了。一旦使其能够被外界访问到,你就得注意到任何调用参数和语义化的变动都可能使其丧失向前的兼容性。作为一般准则,如果不确定是否应该暴露某个属性或对象的话,那就最好别那样做。
那么我们应该怎样来定义多个方法而不至于使命名空间混乱并且保证不被暴露再外呢?这就是闭包的工作,为了便于演示,我们给插件加入了一个叫做 “debug”的功能,它用来记录firebug控制台所选择的网页元素数目。为了创建一个闭包,我们将整个功能的定义放入在一个function中了(有关这方面的知识,可参见JQuery手册)。
JavaScript代码
-
- (function($) {
-
- $.fn.hilight = function(options) {
- debug(this);
- ...
- };
-
- function debug($obj) {
- if (window.console && window.console.log)
- window.console.log('hilight selection count: ' + $obj.size());
- };
- ...
-
- })(jQuery);
debug方法在这里是无法被在插件以外访问到的,因此,我们称之为它是插件私有的。
6.支持元数据插件;
根据你所写的插件的类型,为你的插件加入元数据插件的支持将使其变得更强大。就我个人来说,喜欢采用元数据插件的重要原因便是它可以让你使用定义之外的标签来覆写你的插件属性设置(这在创建demo和例子时十分有用),而且支持它十分的简单。
更新:这部分内容可以在本文的评论中展开讨论(既然有争议的话)
JavaScript代码
-
- $.fn.hilight = function(options) {
- ...
-
- var opts = $.extend({}, $.fn.hilight.defaults, options);
- return this.each(function() {
- var $this = $(this);
-
- var o = $.meta ? $.extend({}, opts, $this.data()) : opts;
- ...
改动部分的代码会做如下的事情:
*测试metadata插件是否可用
*如果可以,将用metadata扩展options对象
这被加入到jQuery.extend,作为其最后一个参数,所以它可以覆写任何其他参数设置。现在我们可以通过下面的方式控制其行为:
XML/HTML代码
-
- <div class="hilight { background: 'red', foreground: 'white' }">Have a nice day!</div>
- <div class="hilight { foreground: 'orange' }">Have a nice day!</div>
- <div class="hilight { background: 'green' }">Have a nice day!</div>
而在调用方面,我们通过一行简单的代码就可以实现预期的效果:
$(’.hilight’).hilight();
将上面所述内容涉及到的代码放在一起,完整的例子程序代码如下:
JavaScript代码
-
-
-
- (function($) {
-
-
-
- $.fn.hilight = function(options) {
- debug(this);
-
- var opts = $.extend({}, $.fn.hilight.defaults, options);
-
- return this.each(function() {
- $this = $(this);
-
- var o = $.meta ? $.extend({}, opts, $this.data()) : opts;
-
- $this.css({
- backgroundColor: o.background,
- color: o.foreground
- });
- var markup = $this.html();
-
- markup = $.fn.hilight.format(markup);
- $this.html(markup);
- });
- };
-
-
-
- function debug($obj) {
- if (window.console && window.console.log)
- window.console.log('hilight selection count: ' + $obj.size());
- };
-
-
-
- $.fn.hilight.format = function(txt) {
- return '<strong>' + txt + '</strong>';
- };
-
-
-
- $.fn.hilight.defaults = {
- foreground: 'red',
- background: 'yellow'
- };
-
-
-
- })(jQuery);
看完以上的内容,你是不是对jQuery的插件开发加深了一定的了解了呢?
顺便提一下,yhustc在自己的博客上写了一个基于jQuery的软键盘插件,你是不是又可以根据上面的教程来写一个基于$.fn.extends的扩展呢?让yhustc的插件可以绑定在某个元件上?
Tags: jquery, 开发, 插件, yhustc
Javascript | 评论:2
| 阅读:24675
Submitted by gouki on 2009, March 15, 8:48 PM
本例子来自博客园,其实关于Autocompleted的例子,用jquery的话,是很多很多,但那都是国外的,难得有国内的例子,看到了就复制回来一份,呵呵
原文:http://www.cnblogs.com/cntlis/archive/2009/03/14/1412144.html
数据采用JSON,格式为{keylist:[{'keyname':关键字1,'keyextend':扩展文字(譬如说结果数目)},{'keyname':关键字2,'keyextend':扩展文字(譬如说结果数目)}]}
JS代码(当成JS代码插入的时候,高亮会超时,所以,用HTML格式了一下)
- /**//*
- * jQuery AutoComplete
- *
- * Author: cntlis
- * http://blog.csdn.net/cntlis
- *
- * Licensed like jQuery, see http://docs.jquery.com/License
- *
- * 作者:cntlis
- * QQ:8112857
- */
- $.fn.AutoComplete = function(url,option){
- var me= this;
- var strKey= $(me).val();
- var strKeyBak= "";
- var isShow = false;
- var doption={
- iwidth: '0px', //下拉框的宽度
- iLengthLower: 1, //当表单的长度大于iLengthLower小于iLengthUpper时才开始执行搜索
- iLengthUpper: 50,
- strPara: "Keyword", //变量名称
- zIndex: 1, //提示框的Z-INDEX值
- hasscroll: 0, //是否出现滚动条0无1有
- hasclose: 1, //是否拥有关闭窗口
- desfun:function(){}
- };
-
- $.extend(doption,option);
- var iLengthLower= doption.iLengthLower;
- var iLengthUpper= doption.iLengthUpper;
- var strPara= doption.strPara;
- if ($("#autocomplete").length<1){$("body").append("<div id='autocomplete' class='autocompletefloor'></div>");}
- $("#autocomplete").hide();
-
- $(me).keyup(function(e){keysearch(e.keyCode);});
- $(me).keydown(function(e){LineSelect(e.keyCode);});
- $(me).bind("blur",function(){
- strKeyBak= $("#autocomplete ul .selected .keyname").text(); //为click事件增加处理
- if (strKeyBak.length>0 && strKeyBak!=$(me).val()){
- $(me).val(strKeyBak);
- doption.desfun();
- };
- floorHide();
- });
-
- var encode=function(v){//如果包含中文就escape,避免重复escape)
- return escape(v).replace(/\+/g, '%2B').replace(/\"/g,'%22').replace(/\'/g, '%27').replace(/\//g,'%2F');
- }
-
- function floorHide(){
- $("#autocomplete").hide().html("");
- strKey= "";
- isShow = false;
- }
-
- function floorShow(){
- var p= $(me).offset();
- var w= (doption.iwidth == "0px") ? $(me).width()+2 : doption.iwidth;
- $("#autocomplete").css({
- 'z-index:':doption.zIndex,
- width:w,
- top:parseInt(p.top+$(me).outerHeight())+"px",
- left:parseInt(p.left)+"px"
- }).show();
- strKey= "";
- isShow = true;
- }
-
- function keysearch(code){
- var strKeyNow=$(me).val();
- if(code == 38 || code == 40 || code == 13 || code == 27 || code == 9) return; //TAB/回车、ESC、向上、向下
- if((strKey == "" || strKeyNow != strKey) && strKeyNow.length >= iLengthLower && strKeyNow.length <= iLengthUpper){
- $.ajax({
- type: "Get",
- dataType: "json",
- url: url,
- data: strPara != "" ? strPara + "=" + encode(strKeyNow) : "",
- success: function(json){
- jsonjson=json.keylist;
- if (json.length>0){
- //获取搜索数据
- var strContent= "<ul>";
- $.each(json, function(i, n){
- if(n.keyname.length>0){ //如果
- //alert(n.keyname);
- strContent+= '<li class="keyinfo"><span class="keyname">'+n.keyname+'</span>';
- try{
- if (n.keyextend.length>0){strContent+='<span class="keyextend">'+n.keyextend+'</span>';}
- }catch(E){};
- strContent+= '</li>';
- };
- });
- if (doption.hasclose==1){
- strContent+= '<li class="close"><span>关闭</span></li>';
- }
- strContent+='</ul>';
- $("#autocomplete").html(strContent);
- $("#autocomplete .keyinfo").mouseover(function(){$("#autocomplete .selected").removeClass("selected");$(this).removeClass("unselected").addClass("selected");}).mouseout(function(){$(this).removeClass("selected").addClass("unselected");}).click(function(){if(strKeyBak.length()>0){$(me).val(strKeyBak);}});
- floorShow();
- }else{
- //没有搜索数据
- floorHide();
- return;
- }
- }
- });
- strKey=$(me).val();
- }
- if(strKey.length == 0 || strKey.length <= iLengthLower || strKey.length >= iLengthUpper) floorHide();
- }
-
- function LineSelect(code){
- if(code == 27){floorHide();};//回车键、ESC键
- if(code == 13){floorHide();doption.desfun();};
- if(!isShow) return;
- ObjSelected=$("#autocomplete ul .selected");
- if (ObjSelected.length>0){ //如果已经有选定
- //alert('dasfdas');
- if(code == 38){ //向上键
- if(ObjSelected.prev().text() != ""){ //如果不是第一个数据
- ObjSelected.removeClass("selected").addClass("unselected").prev().removeClass("unselected").addClass("selected");
- $(me).val($("#autocomplete ul .selected .keyname").text());
- }else{
- ObjSelected.removeClass("selected").addClass("unselected");
- $("#autocomplete .keyinfo:last").removeClass("unselected").addClass("selected");
- $(me).val($("#autocomplete ul .selected .keyname").text());
- }
- }else if (code == 40){ //向下键
- if(ObjSelected.next().text() != ""){ //如果不是第一个数据
- ObjSelected.removeClass("selected").addClass("unselected").next().removeClass("unselected").addClass("selected");
- $(me).val($("#autocomplete ul .selected .keyname").text());
- }else{
- ObjSelected.removeClass("selected").addClass("unselected");
- $("#autocomplete .keyinfo:first").removeClass("unselected").addClass("selected");
- $(me).val($("#autocomplete ul .selected .keyname").text());
- }
- }
- }else if(code == 38){
- $("#autocomplete .keyinfo:last").removeClass("unselected").addClass("selected");
- $(me).val($("#autocomplete .keyinfo:last .keyname").text());
- }else if(code == 40){
- $("#autocomplete .keyinfo:first").removeClass("unselected").addClass("selected");
- $(me).val($("#autocomplete .keyinfo:first .keyname").text());
- }
- }
- }
CSS代码
- #autocomplete{}{ border: 1px solid #000; position: absolute; }
- /**//*每行的格式*/
- #autocomplete li{}{ display: block; text-align: left; height: 20px; line-height: 20px; background-color: #fff; cursor: default; padding: 0 5px; clear: both; }
- /**//*鼠标选中时的格式*/
- #autocomplete .selected{}{ background-color: #565da9; color: #fff; overflow: hidden; }
- /**//*鼠标离开时代格式*/
- #autocomplete .unselected{}{ background-color: #fff; color: #666; }
- /**//*关键字信息*/
- #autocomplete .keyname{}{ display: block; float: left; }
- /**//*关键字扩展信息*/
- #autocomplete .keyextend{}{ display: block; float: right; color: green; }
- #autocomplete .unselected .keyextend{}{ color: green; }
- #autocomplete .selected .keyextend{}{ color: #fff; }
- /**//*关闭*/
- #autocomplete .close{}{ text-align: right; }
- #autocomplete .close span{}{ color: blue; cursor: pointer; text-decoration: underline; }
调用范例
$("#Keyword").AutoComplete("search.asp");
是不是很简单的?只是search.asp返回的数据要是上面所提供的格式,对于PHP来说就太方便了,只要生成相应的数组,然后json_encode一下就全出来了,呵呵
Tags: jquery, autocompleted
Javascript | 评论:4
| 阅读:25678
Submitted by gouki on 2009, March 12, 10:23 PM
继jQuery 1.3.1发布之后,UI包一直没跟上,3月6日,兼容jQuery 1.3.1的UI包终于发布了。大家可以来这里下载jquery-ui-1.7.custom.zip,svn方式:http://jquery-ui.googlecode.com/svn/tags/1.7/
这次改动主要如下:
-----------------
- 出处:http://kaima.cnblogs.com
- 作者:kai.ma
Tags: jquery, ui
Javascript | 评论:0
| 阅读:21611
Submitted by gouki on 2009, February 27, 10:15 AM
这个插件很实用,适合那种写着教程内容的页面,以前如果有多个标题的话,必须一个一个的加锚点,然后再加链接,这样不太方便。所以看到这个插件的时候,我忍不住记录下来。
代码我没有细看,我只看了demo,自己觉得还是很有用的。
代码来自CSSRain.cn,图片同样来自他那里。
网站说:
XML/HTML代码
- 首先根据文章 自动在 右上角生成 导航菜单,
- 然后导航菜单可以 跟随 滚动条滚动 ,实时导航。
- 导航采用平滑方式,更人性化。
-
- 演示:
- http://cssrain.cn/demo/createTitle/title.html
-
- 下载:
- http://cssrain.cn/demo/createTitle/createTitle.rar
-
- 有问题 请留言, 只测试了 google 和 firefox 。
不过,IE或者其他的一些多窗口版的浏览器,会不会把它当成AD层屏蔽掉呢?
截图:
源码分流:
createtitle.rar
Tags: jquery, plugins, 导航
Javascript | 评论:0
| 阅读:19987
Submitted by gouki on 2009, February 17, 9:47 AM
在jQuery的插件库里面,有一个imageLazyLoader,是专门用来进行延迟加载图片的。这次看到这个对于JS,CSS,IMAGE三者都能进行延迟加载的代码,当然是用来查看一下的。代码很小,大约只有3K左右,100多行代码,却实现了这么多的效果,看来jQuery的功能是被挖掘的越来越多了。
JavaScript代码
-
-
-
-
-
-
-
-
-
- ;(function($){
- $.xLazyLoader = function ( method, options ) {
- if (typeof method == 'object') {
- options = method;
- method = 'load';
- };
- xLazyLoader[method]( options );
- };
-
- var xLazyLoader = new function ()
- {
- var head = document.getElementsByTagName("head")[0];
- this.load = function ( options )
- {
-
- var d = {
- js: [],
- css: [],
- image: [],
- name: null,
- load: function(){}
- };
- $.extend(d, options);
-
- var self = this,
- ready = false,
- loaded = {
- js: [],
- css: [],
- image: []
- }
- ;
-
- each('js', d.js);
- each('css', d.css);
- each('image', d.image);
-
- function each (type, urls)
- {
- if ( $.isArray(urls) && urls.length>0 )
- $.each( urls, function(i, url){
- load(type, url);
- });
- else if (typeof urls == 'string')
- load(type, urls);
- };
- function load (type, url)
- {
- self[type](url, function() {
- $.isArray(d[type]) ? loaded[type].push(url) : loaded[type] = url;
- d.js.length == loaded.js.length
- && d.css.length == loaded.css.length
- && d.image.length == loaded.image.length
- && d.load.apply(loaded, []);
- return;
- }, d.name ?'lazy-loaded-'+ d.name : 'lazy-loaded-'+new Date().getTime());
- };
- };
-
- this.js = function (src, callback, name)
- {
- if ($('script[src*="'+src+'"]').length>0) {
- callback();
- return;
- };
- var script = document.createElement('script');
- script.setAttribute("type","text/javascript");
- script.setAttribute("src", src);
- script.setAttribute('id', name);
- if ($.browser.msie)
- script.onreadystatechange = function () {
- /loaded|complete/.test(script.readyState) && callback();
- }
- else
-
- script.onload = callback;
- head.appendChild(script);
- };
-
- this.css = function (href, callback, name)
- {
- if ($('link[href*="'+href+'"]').length>0) {
- callback();
- return;
- };
-
- var link = $('<link rel="stylesheet" type="text/css" media="all" href="'+href+'" id="'+name+'"></link>')[0];
- if ($.browser.msie)
- link.onreadystatechange = function () {
- /loaded|complete/.test(link.readyState) && callback();
- }
- else if ($.browser.opera)
- link.onload = callback;
- else
-
- (function(){
- try {
- link.sheet.cssRule;
- } catch(e){
- setTimeout(arguments.callee, 20);
- return;
- };
- callback();
- })();
- head.appendChild(link);
- };
-
- this.image = function (src, callback)
- {
- var img = new Image();
- img.onload = callback;
- img.src = src;
- };
-
- this.disable = function ( name )
- {
- $('#lazy-loaded-'+name, head).attr('disabled', 'disabled');
- };
- this.enable = function ( name )
- {
- $('#lazy-loaded-'+name, head).removeAttr('disabled');
- };
-
- this.destroy = function ( name )
- {
- $('#lazy-loaded-'+name, head).remove();
- };
- };
- })(jQuery);
使用方法也很方便 :
Load some files
$.xLazyLoader({
js: 'jquery.ui.all.js',
css: 'ui.allplugins.css',
image: 'your_image.jpg',
load: function(){
alert('All files are loaded');
}
});
Load multiple files of each type
$.xLazyLoader({
js: ['ui.core.js','ui.dialog.js'],
css: ['ui.core.css', 'ui.dialog.css'],
image: ['your_image.jpg', 'your_image1.jpg', 'your_image2.jpg'],
name: 'dialog',
load: function(){
alert('All files are loaded');
}
});
Remove tags from head (javascript is still working, but css is completely destroyed )
$.xLazyLoader('destroy','dialog');
Disable css.
$.xLazyLoader('disable','dialog');
Enable css.
$.xLazyLoader('enable','dialog');
项目存在于GOOGLECODE上,网址为:http://code.google.com/p/ajaxsoft/
估计还是会有BUG,不过相信作者应该会更新的
Tags: jquery, lazyloader
Javascript | 评论:0
| 阅读:23203