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 相关文章推荐
基于jquery的防止大图片撑破页面的实现代码(立即缩放)
Oct 24 Javascript
js 金额文本框实现代码
Feb 14 Javascript
一个支持任意尺寸的图片上下左右滑动效果
Aug 24 Javascript
jquery判断复选框是否被选中的方法
Oct 16 Javascript
smartupload实现文件上传时获取表单数据(推荐)
Dec 12 Javascript
js时间查询插件使用详解
Apr 07 Javascript
angular实现图片懒加载实例代码
Jun 08 Javascript
Vuejs 2.0 子组件访问/调用父组件的方法(示例代码)
Feb 08 Javascript
JS实现二维数组横纵列转置的方法
Apr 17 Javascript
JavaScript设计模式之工厂模式简单实例教程
Jul 03 Javascript
最全vue的vue-amap使用高德地图插件画多边形范围的示例代码
Jul 17 Javascript
WebStorm中如何将自己的代码上传到github示例详解
Oct 28 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 页面跳转到另一个页面的多种方法方法总结
2009/07/07 PHP
PHP企业级应用之常见缓存技术篇
2011/01/27 PHP
分享8个最佳的代码片段在线测试网站
2013/06/29 PHP
Yii2针对游客、用户防范规则和限制的解决方法分析
2016/10/08 PHP
PHP操作Redis常用命令的实例详解
2020/12/23 PHP
js弹出层之1:JQuery.Boxy (二)
2011/10/06 Javascript
javascript中自定义对象的属性方法分享
2013/07/12 Javascript
jquery eval解析JSON中的注意点介绍
2013/08/23 Javascript
原生javascript实现的分页插件pagenav
2014/08/28 Javascript
js由下向上不断上升冒气泡效果实例
2015/05/07 Javascript
Javascript闭包与函数柯里化浅析
2016/06/22 Javascript
Vue.js每天必学之计算属性computed与$watch
2016/09/05 Javascript
基于js实现的限制文本框只可以输入数字
2016/12/05 Javascript
详解javascript获取url信息的常见方法
2016/12/19 Javascript
Bootstrap table右键功能实现方法
2017/02/20 Javascript
认识less和webstrom的less配置方法
2017/08/02 Javascript
node.js中axios使用心得总结
2017/11/29 Javascript
jQuery实现简单复制json对象和json对象集合操作示例
2018/07/09 jQuery
CSS3 动画卡顿性能优化的完美解决方案
2018/09/20 Javascript
Vue实现数据请求拦截
2019/10/23 Javascript
Python开发最牛逼的IDE——pycharm
2018/08/01 Python
Python 调用 Outlook 发送邮件过程解析
2019/08/08 Python
Python多线程多进程实例对比解析
2020/03/12 Python
HTML5逐步分析实现拖放功能的方法
2020/09/30 HTML / CSS
英国在线购买马术服装:EQUUS
2019/07/12 全球购物
新浪网技术部笔试题
2016/08/26 面试题
为什么要优先使用同步代码块而不是同步方法?
2013/01/30 面试题
国际政治个人自荐信范文
2013/11/26 职场文书
自我评价范文点评
2013/12/04 职场文书
保险内勤岗位职责
2014/04/05 职场文书
销售经理工作失职检讨书
2014/10/24 职场文书
2015年司机年终工作总结
2015/05/14 职场文书
2019最新婚庆对联集锦!
2019/07/10 职场文书
spring cloud 配置中心客户端启动遇到的问题
2021/09/25 Java/Android
Python 正则模块详情
2021/11/02 Python
使用HBuilder制作一个简单的HTML5网页
2022/07/07 HTML / CSS