jQuery插件制作的实例教程


Posted in Javascript onMay 16, 2016

一、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 + "\n最小值是:" + 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 + "\n最小值是:" + 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 + "\n最小值是:" + 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({//写法1
          a: function(h){
            $("#ad").html(h);
          },
          b:function(h){
            alert(h);
          }
        })
      })(jQuery);
 
      (function($) {//写法2
        jQuery.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 = {
      // string
      sId : "",
      // num
      nWidth : 400,
      // bollean
      bDisplayHeader : true,
      // object
      oContentHtml : "",
      // function
      fCloseCallback : 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,返回1
alert(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._timedHide
MemberCard.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); // true

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); // 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); // true

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); // 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); // true

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); // true

Javascript 相关文章推荐
Javascript结合css实现网页换肤功能
Nov 02 Javascript
jQuery UI-Draggable 参数集合
Jan 10 Javascript
DWZ刷新dialog解决方法
Mar 03 Javascript
JavaScript避免内存泄露及内存管理技巧
Sep 05 Javascript
Angularjs 基础入门
Dec 26 Javascript
jQuery中not()方法用法实例
Jan 06 Javascript
jQuery中extend函数详解
Feb 13 Javascript
jQuery使用hide方法隐藏页面上指定元素的方法
Mar 30 Javascript
封装属于自己的JS组件
Jan 27 Javascript
实践中学习AngularJS表单
Mar 21 Javascript
巧用canvas
Jan 21 Javascript
详解如何在微信小程序中愉快地使用sass
Jul 30 Javascript
总结jQuery插件开发中的一些要点
May 16 #Javascript
JavaScript编写Chrome扩展实现与浏览器的交互及时间通知
May 16 #Javascript
动态的9*9乘法表效果的实现代码
May 16 #Javascript
Svg.js实例教程及使用手册详解(一)
May 16 #Javascript
限制只能输入数字的实现代码
May 16 #Javascript
JavaScript开发Chrome浏览器扩展程序UI的教程
May 16 #Javascript
基于javascript实现最简单的选项卡切换效果
May 16 #Javascript
You might like
PHP实时显示输出
2008/10/02 PHP
php中使用getimagesize获取图片、flash等文件的尺寸信息实例
2014/04/29 PHP
php使用ereg验证文件上传的方法
2014/12/16 PHP
js页面跳转常用的几种方式
2010/11/25 Javascript
js 函数调用模式小结
2011/12/26 Javascript
JQuery中根据属性或属性值获得元素(6种情况获取方法)
2013/01/17 Javascript
JS获取键盘上任意按键的值(实例代码)
2013/11/12 Javascript
文本框文本自动补全效果示例分享
2014/01/19 Javascript
jquery实现一个简单好用的弹出框
2014/09/26 Javascript
微信小程序实现滑动删除效果
2017/05/19 Javascript
vue init webpack myproject构建项目 ip不能访问的解决方法
2018/03/20 Javascript
Vue中android4.4不兼容问题的解决方法
2018/09/04 Javascript
vue-router的两种模式的区别
2019/05/30 Javascript
javascript实现时间日期的格式化的方法汇总
2020/08/06 Javascript
JavaScript实现网页下拉菜单效果
2020/11/20 Javascript
python下paramiko模块实现ssh连接登录Linux服务器
2015/06/03 Python
Python中字典的基础知识归纳小结
2015/08/19 Python
python中WSGI是什么,Python应用WSGI详解
2017/11/24 Python
浅谈python str.format与制表符\t关于中文对齐的细节问题
2019/01/14 Python
Scrapy-Redis结合POST请求获取数据的方法示例
2019/05/07 Python
django页面跳转问题及注意事项
2019/07/18 Python
python 实现查询Neo4j多节点的多层关系
2019/12/23 Python
Python爬虫爬取百度搜索内容代码实例
2020/06/05 Python
Python如何实现大型数组运算(使用NumPy)
2020/07/24 Python
Python 如何定义匿名或内联函数
2020/08/01 Python
详解Django中异步任务之django-celery
2020/11/05 Python
想学画画?python满足你!
2020/12/24 Python
墨西哥巴士车票在线购买:ClickBus
2018/03/27 全球购物
新奥尔良珠宝:Mignon Faget
2020/11/23 全球购物
使用C#编写创建一个线程的代码
2013/01/22 面试题
中介业务员岗位职责
2014/04/09 职场文书
实习生工作证明范本
2014/09/14 职场文书
病人家属写给医院的感谢信
2015/01/23 职场文书
上班迟到检讨书范文
2015/05/06 职场文书
浅谈Python基础之列表那些事儿
2021/05/11 Python
mysql insert 存在即不插入语法说明
2022/03/25 MySQL