本文将通过一个实例来引出jQuery插件开发中的一些细节,首先介绍下jQuery插件开发的一些基础知识。
jQuery的插件开发主要分为两类:
1. 类级别,即在jQuery类本身上扩展方法,类似与 $.ajax,$.get 等。
2. 对象级别,这里所谓的对象是指通过jQuery选择器选中的jQuery对象,在该对象上添加方法。例如:$('div').css(),$('div').show() 等。
在实际开发中,我们通常选用对象级别的方法来开发插件,jQuery强大的选择器及对象操作是我们选择这种方式很大的一个原因。
接下来我们看看两种方式的具体写法是什么:
类级别的插件开发
调用
$.foo();
在这里,对扩展方法的命名需要考究一些,以免与jQuery类中的原有方法重名。即便如此,当我们需要在类上扩展多个方法时仍有可能会出现命名冲突的情况,为此我们可以创建自定义的命名空间:
调用
$.myPulgin.foo();
调用(假设拥有一个id为obj的元素)
$('#obj').foo();
有个会问 fn 是什么东东?粘一段别人
截取的jQuery源码就明白了:
jQuery.fn = jQuery.prototype = {
init: function(selector,context) {
//....
}
}
原来是原型链啊。。。
接收配置参数
在编写一个插件时,我们可以让使用插件的人能按自己的意愿设置插件的一些属性,这就需要插件有接收参数的功能,同时当使用插件的人不传入参数时,插件内部也有一套自己默认的配置参数。
这里的关键就是 $.extend 方法,它能够将对象进行合并。对于相同的属性,后面的对象会覆盖前面的对象。为什么extend方法第一个参数是一个空对象呢?因为该方法会将后者合并到前者上,为了不让 defaults 被改变所以第一个参数设为空对象。
如果我们允许使用插件的人能够设置默认参数,就需要将其暴露出来:
这样就可以在外部对插件的默认参数进行修改了。
适当的暴露一些方法
改写:
暴露插件中的一部分方法是很牛逼的,它使得别人可以对你的方法进行扩展、覆盖。但是当别人对你的参数或方法进行修改时,很可能会影响其他很多东西。所以在考虑要不要暴露方法时候要头脑清楚,不确定的就不要暴露了。
保持函数的私有性
说到保持私有性,首先想到什么?没错,就是闭包:
这是jQuery官方给出的插件开发方式,好处包括:1.没有全局依赖 2.避免其他人破坏 3.兼容 '$' 与 'jQuery' 操作符。
如上,debug 方法就成了插件内部的私有方法,外部无法对其进行修改。在闭包前面加 ; 是防止进行代码合并时,如果闭包前的代码缺少分号从而导致后面报错的情况。
合并
插件
$.fn.foo = function(options) {
//doSomething...
}
//私有
函数
function debug() {
//doSomething...
}
//定义暴露
函数
$.fn.foo.sayColor = function() {
//doSomething...
}
//
插件默认参数
$.fn.foo.default = {
color: '#000',backgroundColor: 'red'
}
})(jQuery);
以上的代码就创建了一个完整且规范的插件骨架,看起来虽然很简单但在实际开发中还是有很多技巧与注意事项,接下来我们通过一个实例来看看。
想了半天,觉得将弹窗做成插件当作示例是比较合适的。在开发之前我们先构想一下这个弹窗插件的结构与功能等:
从上图我们看出包括三个部分,标题、内容、以及按钮组。这里需要申明一点,我们不想只做成浏览器里默认的只包含一个按钮的alert框,而是使用者可以自定义按钮数量,这样该弹出框也能完成类似confirm框的功能。
1. 我们创建了基于对象且名为 popWin 方法,并将 defaults 默认配置参数暴露出去以便使用的人进行修改;
2. 这里使用面向对象的方法来管理我们的私有函数,createPopWin 方法就是我们私有的用来创建弹窗的函数。
3. 在插件被调用时将jq对象与自定义的参数传入构造函数中并实例化。
设想一下我们该怎么调用这个插件呢?我们可以在自己的文档树中合适的位置插入一个 div 元素,选中该 div 并调用我们定义在jQuery对象上的 popWin 方法。
调用 popWin 的同时传入自定义的配置参数,之后被选中的 div 元素就被神奇的转化成一个弹窗了!当然,这只是我们的设想,下面开始码代码。
确定默认配置
标题',//
标题
desc: '描述',//描述
winCssName: 'pop-win',//弹窗的CSS类名
titleCssName: 'pop-title',//
标题区域的CSS类名
descCssName: 'pop-desc',//描述区域的CSS类名
btnAreaCssName: 'pop-btn-
Box',//按钮区域的CSS类名
btnCssName: 'pop-btn',//单个按钮的CSS类名
btnArr: ['确定'],//按钮组
callback: function(){} //点击按钮之后的回调
函数
}
我们定义了如上的参数,为什么有要传入这么多的CSS类名呢?1. 为了保证JS与CSS尽可能的解耦。 2. 你的样式有很大可能别人并不适用。所以你需要配置一份样式表文件来对应你的默认类名,当别人需要更改样式时可以传入自己编写的样式。
按钮组为一个数组,我们的弹窗需要根据其传入的数组长度来动态的生成若干个按钮。回调函数的作用是在用户点击了某个按钮时返回他所点击按钮的索引值,方便他进行后续的操作。
弹窗DOM创建
').css({
width: this.opts.width, height: this.opts.height, position: 'absolute', top: '30%', left: '50%', marginLeft: '-' + (this.opts.width.split('px')[0] / 2) + 'px'
}).attr('class',this.opts.winCssName);
//
标题区域
titleAreaDom = popWinDom.find('div:eq(0)')
.text(this.opts.title)
.attr('class',this.opts.titleCssName);
//描述区域
descAreaDom = popWinDom.find('div:eq(1)')
.text(this.opts.desc)
.attr('class',this.opts.descCssName);
//按钮区域
btnAreaDom = popWinDom.find('div:eq(2)')
.attr('class',this.opts.btnAreaCssName);
//插入按钮
this.opts.btnArr.map(function(item,index) {
btnAreaDom.append($('
')
.text(item)
.attr({'data-index':index,'class':_this.opts.btnCssName})
.on('click',function() {
_this.opts.callback($(this).attr('data-index'));
}));
});
this.$ele.append(popWinDom);
}
}
1. 首先命名了四个变量用来缓存我们将要创建的四个DOM,将传入的jQuery对象变形成覆盖整个窗口半透明元素;
').css({
width: this.opts.width,height: this.opts.height,position: 'absolute',top: '30%',left: '50%',marginLeft: '-' + (this.opts.width.split('px')[0] / 2) + 'px'
}).attr('class',this.opts.winCssName).on('click',function(event) {
event.stopPropagation();
});