- 未命名 -

 

如果你是一名jQuery开发者,那么你一定也想把自己最自鸣得意的函数封装成jQuery插件。通过插件来扩展jQuery库将会提高你的团队的开发效率。下面让我们来撰写我们的第一个jQuery插件。

这项工作容易得出乎你的想象。首先,向jQuery.fn对象添加一个函数成员,这个函数成员的名称就是你的插件名称:

jQuery.fn.myPlugin = function() {

// 在这里自由发挥

};

你也可以使用$符号来代替jQuery这个变量,但是这容易和其他JS库的命名空间产生冲突。我们使用一个立即函数调用来避免这一问题:

(function($) {

$.fn.myPlugin = function() {

// 在这里自由发挥

};

})(jQuery);

现在我们已经写好了插件外壳,可以向其中填充真正的工作代码了。注意,在插件的函数的作用域中,this关键字已经指向了一个jQuery对象,无需对这个this进行再封装了。

(function($) {

$.fn.myPlugin = function() {

// this已经指向了一个jQuery对象

// 没有必要使用$(this)进行再封装了

this.fadeIn('normal', function() {

// 这里的this指向一个DOM对象

});

};

})(jQuery);

$('#element').myPlugin();

让我们写一个实际有用的插件:

(function($) {

$.fn.maxHeight = function() {

var max = 0;

this.each(function() {

max = Math.max(max, $(this).height());

});

return max;

};

})(jQuery);

var tallest = $('div').maxHeight();

jQuery库中最有魅力的特性就是事件的链式调用特性。为了让我们的插件也拥有这一特性,需要我们的插件调用结束后返回this关键字:

(function($) {

$.fn.lockDimensions = function(type) {

return this.each(function() {

var $this = $(this);

if (!type || type == 'width') {

$this.width($this.width());

}

if (!type || type == 'height') {

$this.height($this.height());

}

});

};

})(jQuery);

$('div').lockDimensions('width').css('color', 'red');

对于需要使用众多选项来配置的插件,我们可以提供一套默认选项,并通过$.extend来获取到用户的调用选项:

(function($) {

$.fn.tooltip = function(options) {

var settings = $extend({

'location': 'top',

'background-color': 'blue'

}, options);

return this.each(function() {

// 工作代码

});

};

})(jQuery);

$('div').tooltip({

'location': 'left'

});

本例中,在插件调用之后,location选项的值被'left'覆盖,background-color的值仍然为默认值'blue'。

我们已经知道了如何撰写一款高可配置性的插件。接下来一个重要的话题是命名空间。为你的插件设置恰当的命名空间将会使你的生活保持美好,绝对不要在一个插件中声明多个命名空间。下面是一个不好的实践:

(function($) {

$.fn.tooltip = function(options) {

// THIS

};

$.fn.tooltipShow = function() {

// IS

};

$.fn.tooltipHide = function() {

// BAD

};

$.fn.tooltipUpdate = function(content) {

// !!!

};

})(jQuery);

正确的做法是把所有的方法集中到一个字面量对象中,然后通过参数传递方法名称进行调用。

(function($) {

var methods = {

init: function(options) {

// THIS

},

show: function() {

// IS

},

hide: function() {

// GOOD

},

update: function(content) {

// !!!

}

};

$.fn.tooltip = function(method) {

if (methods[method]) {

return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));

} else if (typeof method === 'object' || !method) {

return methods.init.apply(this, arguments);

} else {

$.error('Method ' + method + ' does not exist on jQuery.tooltip');

}

};

})(jQuery);

// calls the init method

$('div').tooltip();

// calls the init method

$('div').tooltip({

foo: 'bar'

});

// calls the hide method

$('div').tooltip('hide');

// calls the update method

$('div').tooltip('update', 'This is the new tooltip content!');

在上面的例子中,我们把所有的方法都封装在了插件的闭包中,通过传递方法名作为调用的第一个参数来调用特定的方法。额外的参数将会作为方法参数来使用。这是jQuery插件社区的标准封装方式。

bind方法的一个不为人知的特性是,它允许在命名空间中绑定事件。这样做的好处是,当你需要unbind这个事件的时候,不会影响到在命名空间外绑定的相同的事件。简单的在事件名称后面添加“.<namespace>”就可以了:

(function($) {

var methods = {

init: function(options) {

return this.each(function() {

$(window).bind('resize.tooltip', methods.reposition);

});

},

destroy: function() {

return this.each(function() {

$(window).unbind('.tooltip');

});

},

reposition: function() {

// do something

},

show: function() {

// do something

},

hide: function() {

// do something

},

update: function(content) {

// do something

}

};

$.fn.tooltip = function(method) {

if (methods[method]) {

return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));

} else if (typeof method === 'object' || !method) {

return methods.init.apply(this, arguments);

} else {

$.error('Method ' + method + ' does not exist on jQuery.tooltip');

}

};

})(jQuery);

$('#fun').tooltip();

// Some time later...

$('#fun').tooltip('destroy');

通常来说,你需要维护插件的状态并且检查插件是否在目标元素上已经被初始化。使用jQuery的data方法可以帮助你很容易地追踪所有的变量。推荐使用一个字面量对象来保持所有的变量。
 

(function($) {

var methods = {

init: function(options) {

return this.each(function() {

var $this = $(this),

data = $this.data('tooltip'),

tooltip = $('<div />', {

text: $this.attr('title')

});

if (!data) {

/*

 * Do more setup stuff here

*/

$(this).data('tooltip', {

target: $this,

tooltip: tooltip

});

}

});

},

destroy: function() {

return this.each(function() {

var $this = $(this),

data = $this.data('tooltip');

$(window).unbind('.tooltip');

data.tooltip.remove();

$this.removeData('tooltip');

});

},

reposition: function() {},

show: function() {},

hide: function() {},

update: function(content) {}

};

$.fn.tooltip = ...

})(jQuery);