Welcome 微信登录

首页 / 脚本样式 / JavaScript / jQuery插件制作的实例教程

一、jQuery插件的类型
1. jQuery方法
很大一部分的jQuery插件都是这种类型,由于此类插件是将对象方法封装起来,在jQuery选择器获取jQuery对象过程中进行操作,从而发挥jQuery强大的选择器优势。
2. 全局函数法
可以把自定义的功能函数独立附加到jQuery命名空间下,从而作为jQuery作用域下的一个公共函数使用。
但全局函数没有被绑定到jQuery对象上,故不能在选择器获取的jQuery对象上调用。需要通过jQuery.fn()或$.fn()方式进行引用。
3. 选择器法
如果觉得jQuery提供的选择器不够用或不方便的话,可以考虑自定义选择器。
二、jQuery插件的机制
1.   jQuery.extend()方法
这种方法能够创建全局函数或选择器。
所谓全局函数,就是jQuery对象的方法,实际上就是位于jQuery命名空间内部的函数,有人把这类函数称为实用工具函数,这些函数都有一个共同特征,就是不直接操作DOM元素,而是操作Javascript的非元素对象,或者执行其他非对象的特定操作,如jQuery的each()函数和noConflict()函数。
例如,在jQuery命名空间上创建两个公共函数:


jQuery.extend({ min : function(a,b){ return a<b?a:b; }, max : function(a,b){ return a<b?b:a; } }) $(function(){ $("input").click(function(){ var a = prompt("请输入一个数:"); var b = prompt("再输入一个数:"); var c = jQuery.min(a,b); var d = jQuery.max(a,b); alert("最大值是:" + d + "
最小值是:" + c); }); }) 
<input type="button" value="jQuery扩展测试" />
jQuery.extend({ min : function(a,b){ return a<b?a:b; }, max : function(a,b){ return a<b?b:a; } }) $(function(){ $("input").click(function(){ var a = prompt("请输入一个数:"); var b = prompt("再输入一个数:"); var c = jQuery.min(a,b); var d = jQuery.max(a,b); alert("最大值是:" + d + "
最小值是:" + c); }); }) 
<input type="button" value="jQuery扩展测试" />
jQuery.extend()方法除了可以创建插件外,还可以用来扩展jQuery对象。
例如,调用jQuery.extend()方法把对象a和对象b合并为一个新的对象,并返回合并对象将其赋值给变量c:
var a = {name : "aaa",pass : 777}; var b = {name : "bbb",pass : 888,age : 9}; var c = jQuery.extend(a,b); $(function(){ for(var name in c){ $("div").html($("div").html() + "<br />"+ name + ":" + c[name]); } })


如果要向jQuery命名空间上添加一个函数,只需要将这个新函数制定为jQuery对象的一个属性即可。其中jQuery对象名也可以简写为$,jQuery.smalluv==$.smalluv。
例如,创建jQuery全局函数:
jQuery.smalluv = { min : function(a,b){ return a<b?a:b; }, max : function(a,b){ return a<b?b:a; } } $(function(){ $("input").click(function(){ var a = prompt("请输入一个数:"); var b = prompt("再输入一个数:"); var c = jQuery.smalluv.min(a,b); var d = jQuery.smalluv.max(a,b); alert("最大值是:" + d + "
最小值是:" + c); }); })


2. jQuery.fn.extend()方法
这种方法能够创建jQuery对象方法。
举一个最简单的jQuery对象方法例子:
jQuery.fn.test = function(){ alert("jQuery对象方法"); } $(function(){ $("div").click(function(){ $(this).test(); }); })


三、初步总结
在jQuery匿名函数中,采用jQuery.extend();方法创建jQuery插件
在jQuery匿名函数中,采用对象.属性=函数的方式创建jQuery插件
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><title>最简单的jquery插件</title><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><script type="text/javascript" src="../res/jquery/jquery-1.4.4.min.js"></script><script type="text/javascript">(function($) {jQuery.extend({//写法1a: function(h){$("#ad").html(h);},b:function(h){alert(h);}})})(jQuery); (function($) {//写法2jQuery.a=function(h){$("#ad").html(h);}jQuery.b=function(h){alert(h);}})(jQuery); $(document).ready(function(){$.a("abc");$.b("xyz");}); </script> </head><body><h3>最简单的jQuery插件</h3><div id="ad"></div></body></html>
四、编写实例
写法一
插件主体:
(function($, window){// 初始态定义var _oDialogCollections = {}; // 插件定义$.fn.MNDialog = function (_aoConfig) {// 默认参数,可被重写var defaults = {// stringsId : "",// numnWidth : 400,// bolleanbDisplayHeader : true,// objectoContentHtml : "",// functionfCloseCallback : null}; var _oSelf = this,$this = $(this); // 插件配置this.oConfig = $.extend(defaults, _aoConfig); // 初始化函数var _init = function () {if (_oDialogCollections) {// 对于已初始化的处理// 如果此时已经存在弹框,则remove掉再添加新的弹框}// 初始化弹出框数据_initData();// 事件绑定_loadEvent();// 加载内容_loadContent();}// 私有函数var _initData = function () {};var _loadEvent = function () {};var _loadContent = function () {// 内容(分字符和函数两种,字符为静态模板,函数为异步请求后组装的模板,会延迟,所以特殊处理)if($.isFunction(_oSelf.oConfig.oContentHtml)) {_oSelf.oConfig.oContentHtml.call(_oSelf, function(oCallbackHtml) {// 便于传带参函数进来并且执行_oSelf.html(oCallbackHtml);// 有回调函数则执行_oSelf.oConfig.fLoadedCallback && _oSelf.oConfig.fLoadedCallback.call(_oSelf, _oSelf._oContainer$);});} else if ($.type(_oSelf.oConfig.oContentHtml) === "string") {_oSelf.html(_oSelf.oConfig.oContentHtml);_oSelf.oConfig.fLoadedCallback && _oSelf.oConfig.fLoadedCallback.call(_oSelf, _oSelf._oContainer$);} else {console.log("弹出框的内容格式不对,应为function或者string。");}}; // 内部使用参数var _oEventAlias = {click : "D_ck",dblclick : "D_dbl"}; // 提供外部函数this.close = function () {_close();} // 启动插件_init(); // 链式调用return this;};// 插件结束})(jQuery, window);
调用
var MNDialog = $("#header").MNDialog({sId : "#footer",//覆盖默认值fCloseCallback : dialog,//回调函数oContentHtml : function(_aoCallback){_aoCallback(_oEditGrpDlgView.el);}}});// 调用提供的函数MNDialog.close;function dialog(){ }
点评
1. 自调用匿名函数
(function($, window) { // jquery code})(jQuery, window);
用处:通过定义一个匿名函数,创建了一个“私有”的命名空间,该命名空间的变量和方法,不会破坏全局的命名空间。这点非常有用也是一个JS框架必须支持的功能,jQuery被应用在成千上万的JavaScript程序中,必须确保jQuery创建的变量不能和导入他的程序所使用的变量发生冲突。
2. 匿名函数为什么要传入window
通过传入window变量,使得window由全局变量变为局部变量,当在jQuery代码块中访问window时,不需要将作用域链回退到顶层作用域,这样可以更快的访问window;这还不是关键所在,更重要的是,将window作为参数传入,可以在压缩代码时进行优化,看看jquery.min.js:
(function(a,b){})(jQuery, window); // jQuery被优化为a, window 被优化为 b
3. 全局变量this定义
var _oSelf = this,$this = $(this);
使得在插件的函数内可以使用指向插件的this
4. 插件配置
this.oConfig = $.extend(defaults, _aoConfig);
设置默认参数,同时也可以再插件定义时传入参数覆盖默认值
5. 初始化函数
一般的插件会有init初始化函数并在插件的尾部初始化
6. 私有函数、公有函数
私有函数:插件内使用,函数名使用”_”作为前缀标识
共有函数:可在插件外使用,函数名使用”this.”作为前缀标识,作为插件的一个方法供外部使用
7. return this
最后返回jQuery对象,便于jQuery的链式操作
写法二
主体结构
(function($){$.fn.addrInput = function(_aoOptions){var _oSelf = this;_oSelf.sVersion = "version-1.0.0";_oSelf.oConfig = {nInputLimitNum : 9};// 插件配置$.extend(_oSelf.oConfig, _aoOptions); // 调用这个对象的方法,传递this$.fn.addrInput._initUI.call(_oSelf, event);$.fn.addrInput._initEvents.call(_oSelf); // 提供外部函数this.close = function () {_close();} //返回jQuery对象,便于Jquery的链式操作return _oSelf;}$.fn.addrInput._initUI = function(event){var _oSelf = this,_oTarget = $(event.currentTarget);}$.fn.addrInput._initEvents = function(){}})(window.jQuery);
点评
1. 美观
插件的方法写在外部,并通过在插件主体传递this的方式调用
2. 定义插件版本号
不过在这里还是没有用到
3. 关于call
这里的第一个参数为传递this,后面的均为参数
语法:
call([thisObj[,arg1[, arg2[, [,.argN]]]]])
定义:调用一个对象的一个方法,以另一个对象替换当前对象。
说明:call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。
4. 关于”this”
在插件的方法中,可能有用到指向插件的this、和指向事件触发的this,所以事件触发的this用event来获取:event.cuerrntTarget
event.currentTarget:指向事件所绑定的元素,按照事件冒泡的方式,向上找到元素
event.target:始终指向事件发生时的元素
如:
html代码
<div id="wrapper"> <a href="#" id="inner">click here!</a> </div>
 js代码
$("#wrapper").click(function(e) { console.log("#wrapper"); console.log(e.currentTarget); console.log(e.target); }); $("#inner").click(function(e) { console.log("#inner"); console.log(e.currentTarget); console.log(e.target); });
结果输出
#inner<a href=​"#" id=​"inner">​click here!​</a>​<a href=​"#" id=​"inner">​click here!​</a>​#wrapper<div id=​"wrapper">​<a href=​"#" id=​"inner">​click here!​</a>​</div>​<a href=​"#" id=​"inner">​click here!​</a>​
写法三(原生写法)
主体结构
var MemberCard = function(_aoOption){// 配置(默认是从外面传进来)_aoOption || (_aoOption = {}) ;// 初始化函数_init(this);} var _init = function(_aoSelf) {// 函数执行_initData(_aoSelf);// 调用对象的私有方法_aoSelf._timedHide();} var _initData = function ( _aoSelf ) {} // 私有方法MemberCard.prototype._timedHide = function(_aoOptions) {var _oSelf = this;clearTimeout(this.iHideTimer);// 使用underscore.js的extend方法来实现属性覆盖var oDefault = extend( { nHideTime: 300 }, _aoOptions );_oSelf.iHideTimer = setTimeout( function(){// 调用对象的共有方法_oSelf.hide();}, oDefault.nHideTime);} // 公有方法MemberCard.prototype.hide = function(_aoOptions) {}
使用
var oColleagueCard = new MemberCard({ nHideTime: 200 });oColleagueCard.hide();
点评:
1. 关于属性覆盖(对象深拷贝)
原生函数实现方法
function getType(o){return ((_t = typeof(o)) == "object" ? o==null && "null" || Object.prototype.toString.call(o).slice(8,-1):_t).toLowerCase();}function extend(destination,source){for(var p in source){if(getType(source[p])=="array"||getType(source[p])=="object"){destination[p]=getType(source[p])=="array"?[]:{};arguments.callee(destination[p],source[p]);}else{destination[p]=source[p];}}}
demo:
var test={a:"ss",b:[1,2,3],c:{d:"css",e:"cdd"}};var test1={};extend(test1,test);test1.b[0]="change"; //改变test1的b属性对象的第0个数组元素alert(test.b[0]); //不影响test,返回1alert(test1.b[0]); //返回change
基于jQuery的实现方法:
jQuery.extend([deep], target, object1, [objectN]);
用一个或多个其他对象来扩展一个对象,返回被扩展的对象。
如果不指定target,则给jQuery命名空间本身进行扩展。这有助于插件作者为jQuery增加新方法。 如果第一个参数设置为true,则jQuery返回一个深层次的副本,递归地复制找到的任何对象。否则的话,副本会与原对象共享结构。 未定义的属性将不会被复制,然而从对象的原型继承的属性将会被复制。
demo:
var options = {id: "nav", class: "header"}var config = $.extend({id: "navi"}, options); //config={id: "nav", class: "header"}
 2. 关于this
这个对象的所有方法的this都指向这个对象,所以就不需要重新指定
写法四
主体结构
function EditorUtil() {this._editorContent = $( "#editor_content" );this._picBtn = $( "#project_pic" );this.ieBookmark = null;}EditorUtil.prototype = {consturctor: EditorUtil, noteBookmark: function() {},htmlReplace: function( text ) {if( typeof text === "string" ) {return text.replace( /[<>"&]/g, function( match, pos, originalText ) {switch( match ) {case "<":return "<";case ">":return ">";case "&":return "&";case """:return """;}});}return "";},init: function() {this._memBtn.bind( "click", function( event ) {$(".error_content").hide();return false;});}}; // 初始化富文本编辑器var editor = new EditorUtil();editor.init();
总结
写法四和写法三其实都差不多,但是你们有没有看出其中的不一样呢?
两种都是利用原型链给对象添加方法:
写法三:
MemberCard.prototype._timedHideMemberCard.prototype.hide
写法四:
EditorUtil.prototype = {consturctor: EditorUtil,noteBookmark: function(){},htmlReplace: function(){}}
细看写法四利用“对象直接量”的写法给EditorUtil对象添加方法,和写法三的区别在于写法四这样写会造成consturctor属性的改变
constructor属性:始终指向创建当前对象的构造函数
每个函数都有一个默认的属性prototype,而这个prototype的constructor默认指向这个函数。如下例所示:
function Person(name) { this.name = name; }; Person.prototype.getName = function() { return this.name; }; var p = new Person("ZhangSan"); console.log(p.constructor === Person); // true console.log(Person.prototype.constructor === Person); // true // 将上两行代码合并就得到如下结果 console.log(p.constructor.prototype.constructor === Person); // truefunction Person(name) { this.name = name; }; Person.prototype.getName = function() { return this.name; }; var p = new Person("ZhangSan");console.log(p.constructor === Person); // true console.log(Person.prototype.constructor === Person); // true // 将上两行代码合并就得到如下结果 console.log(p.constructor.prototype.constructor === Person); // true当时当我们重新定义函数的prototype时(注意:和上例的区别,这里不是修改而是覆盖),constructor属性的行为就有点奇怪了,如下示例:function Person(name) { this.name = name; }; Person.prototype = { getName: function() { return this.name; } }; var p = new Person("ZhangSan"); console.log(p.constructor === Person); // false console.log(Person.prototype.constructor === Person); // false console.log(p.constructor.prototype.constructor === Person); // false
为什么呢?
原来是因为覆盖Person.prototype时,等价于进行如下代码操作:
Person.prototype = new Object({ getName: function() { return this.name; } });Person.prototype = new Object({ getName: function() { return this.name; } });


而constructor属性始终指向创建自身的构造函数,所以此时Person.prototype.constructor === Object,即是:
function Person(name) { this.name = name; }; Person.prototype = { getName: function() { return this.name; } }; var p = new Person("ZhangSan"); console.log(p.constructor === Object); // true console.log(Person.prototype.constructor === Object); // true console.log(p.constructor.prototype.constructor === Object); // truefunction Person(name) { this.name = name; }; Person.prototype = { getName: function() { return this.name; } }; var p = new Person("ZhangSan"); console.log(p.constructor === Object); // true console.log(Person.prototype.constructor === Object); // true console.log(p.constructor.prototype.constructor === Object); // true

怎么修正这种问题呢?方法也很简单,重新覆盖Person.prototype.constructor即可:
function Person(name) { this.name = name; }; Person.prototype = new Object({ getName: function() { return this.name; } }); Person.prototype.constructor = Person; var p = new Person("ZhangSan"); console.log(p.constructor === Person); // true console.log(Person.prototype.constructor === Person); // true console.log(p.constructor.prototype.constructor === Person); // truefunction Person(name) { this.name = name; }; Person.prototype = new Object({ getName: function() { return this.name; } }); Person.prototype.constructor = Person; var p = new Person("ZhangSan"); console.log(p.constructor === Person); // true console.log(Person.prototype.constructor === Person); // true console.log(p.constructor.prototype.constructor === Person); // true