需求场景
最近项目改版中,发现很多地方有这样一个操作(见下图gif动画演示),很多地方都有用到。这里不讨论它的用户体验怎么样。
仅仅是从复用的角度,如果每个页面都去写text和select元素,两个button按钮,增加add和delete对应的js函数,无疑大大增加了工作量和维护成本。
select有预设值的情况:
下面就开始动手把这4个html元素做成一个JQuery插件。
jquery插件结构
如果你是零基础,请参考Jquery官网对jquery plugin的介绍:http://learn.jquery.com/plugins/
整个插件做好后是一个js文件,我们首先来看下它的整体结构,如下图:
调用时的代码(无参):
$(".demo1").dlpcustomSelect();
调用时的代码(有参):
$(".demo1").dlpcustomSelect({
addButtonText:'Add',
delButtonText:'>>'
});
前台HTML:
<select class="demo1"></select>
这样就把插件定义的html元素全部渲染出来了,插件自带了相关js函数和功能。代码维护起来非常方便。
下面我们开始这个Jquery插件的制作过程:
一. 定义插件结构,插件名称,默认值和构造函数
1.插件结构,名称,默认值
新建dlpcustomselect.js文件后,我们首先书写这样的代码:
;(function ($, window, document, undefined) {
//Author:HANGWEI
//Create the defaults once
var pluginName = 'dlpcustomSelect',
defaults = {
addButtonEnabled : true,
addButtonText: 'Add',
delButtonText:'Delete'
};
//... other code ...
// A really lightweight plugin wrapper around the constructor,
// preventing against multiple instantiations
$.fn[ pluginName ] = function (options) {
var args = arguments;
// Is the first parameter an object (options), or was omitted, instantiate a new instance of the plugin.
if (options === undefined || typeof options === 'object') {
return this.each(function () {
// If this is not a select
if (!$(this).is('select')) {
$(this).find('select').each(function(index, item) {
// For each nested select, instantiate the dlp custom select
$(item).dlpcustomSelect(options);//注意此处的插件名称
});
} else if (!$.data(this, 'plugin_' + pluginName)) {
// Only allow the plugin to be instantiated once so we check that the element has no plugin instantiation yet
// if it has no instance, create a new one, pass options to our plugin constructor,
// and store the plugin instance in the elements jQuery data object.
$.data(this, 'plugin_' + pluginName, new DlpCustomSelect(this, options));//注意此处插件的构造函数
}
});
// If the first parameter is a string and it doesn't start with an underscore or "contains" the `init`-function,
// treat this as a call to a public method.
} else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
// Cache the method call to make it possible to return a value
var returns;
this.each(function () {
var instance = $.data(this, 'plugin_' + pluginName);
// Tests that there's already a plugin-instance and checks that the requested public method exists
if (instance instanceof DlpCustomSelect && typeof instance[options] === 'function') {//注意此处插件构造函数名
// Call the method of our plugin instance, and pass it the supplied arguments.
returns = instance[options].apply(instance, Array.prototype.slice.call(args, 1));
}
});
// If the earlier cached method gives a value back return the value,
// otherwise return this to preserve chainability.
return returns !== undefined ? returns : this;
}
};
})(jQuery, window, document);
在上述代码中,pluginName是插件名称,defaults规定了插件的三个参数及其默认值;
$.fn[pluginName]=function(options){};函数的功能,如代码注释所说,阻止多个插件实例被创建。
;(function ($, window, document, undefined) { 这句代码的详细解释请参考 这里 。
2. 构造函数
// The actual plugin constructor
function DlpCustomSelect(element, options) {
this.element = $(element);
// jQuery has an extend method which merges the contents of two or
// more objects, storing the result in the first object. The first object
// is generally empty as we don't want to alter the default options for
// future instances of the plugin
this.settings = $.extend({}, defaults, options);
this._defaults = defaults;
this._name = pluginName;
this.init();
}
构造函数用于初始化和参数定义,如无特殊需求, 可参考上述写法来定义。
二. 编写插件核心部分代码
下面我们开始章节一代码结构中: other code 的部分
1. 构造函数名.prototype={...};
1 DlpCustomSelect.prototype = {View Code
2 init: function () {
3 // Add the custom HTML template
4 this.container = $('' +
5 '<div class="dlpcustomselect-container">' +
6 '<table class="box1">' +
7 ' <tr>' +
8 ' <td><input class="waitAddValue" type="text" size="48" /></td>' +
9 ' <td><input class="btn-pull-buttom" type="button" /></td>' +
10 ' </tr>' +
11 ' <tr>' +
12 ' <td><select style="width: 265px;height: 100px" multiple="multiple"></select></td>' +
13 ' <td><input class="btn-delete-buttom" type="button" /></td>' +
14 ' </tr>' +
15 '</table>'+
16 '</div>')
17 .insertBefore(this.element);
18
19 // Cache the inner elements
20 this.elements = {
21 originalSelect: this.element,
22 box1: $('.box1', this.container),
23 filterInput1: $('.box1 .waitAddValue', this.container),
24 select1: $('.box1 select', this.container),
25 addButton: $('.box1 .btn-pull-buttom', this.container),
26 deleteButton: $('.box1 .btn-delete-buttom', this.container)
27 };
28
29 // Set select IDs
30 this.originalSelectName = this.element.attr('name') || '';
31 var select1Id = 'dlpcustomselect-list_' + this.originalSelectName;
32 this.elements.select1.attr('id', select1Id);
33
34 // Apply all settings
35 this.setAddButtonEnabled(this.settings.addButtonEnabled);
36 this.setAddButtonText(this.settings.addButtonText);
37 this.setDelButtonText(this.settings.delButtonText);
38
39 //updateSelectionStates(this);
40 // Hide the original select
41 this.element.hide();
42
43 bindEvents(this);
44 refreshSelects(this);
45
46 return this.element;
47 },
48 setAddButtonEnabled: function(value, refresh) {
49 this.settings.addButtonEnabled = value;
50 if (value) {
51 this.container.find('.btn-pull-buttom').removeAttr("disabled");
52 } else {
53 this.container.find('.btn-pull-buttom').attr("disabled","disabled");
54 }
55 if (refresh) {
56 //refreshSelects(this);
57 }
58 return this.element;
59 },
60 setAddButtonText: function(value, refresh) {
61 this.settings.addButtonText = value;
62 if (value) {
63 this.elements.addButton.show().val(value);
64 //if upper code type doesn't work,use this code.
65 //this.container.find('.btn-pull-buttom').show().val(value);
66 } else {
67 this.elements.addButton.hide().val(value);
68 //if upper code type doesn't work,use this code.
69 //this.container.find('.btn-pull-buttom').hide().val(value);
70 }
71 if (refresh) {
72 //refreshSelects(this);
73 }
74 return this.element;
75 },
76 setDelButtonText: function(value, refresh) {
77 this.settings.delButtonText = value;
78 if (value) {
79 this.elements.deleteButton.show().val(value);
80 } else {
81 this.elements.deleteButton.hide().val(value);
82 }
83 if (refresh) {
84 //refreshSelects(this);
85 }
86 return this.element;
87 },
88 getCustomData: function(){
89 var terms = new Array();
90 this.container.find('.box1 select option').each(function(index, item) {
91 terms.push(item['value']);
92 });
93 return terms;
94 },
95 getContainer: function() {
96 return this.container;
97 },
98 destroy: function() {
99 this.container.remove();
100 this.element.show();
101 $.data(this, 'plugin_' + pluginName, null);
102 return this.element;
103 }
104 };
init:function(){...} 需要实现的init函数,这里我们用来创建html模板、应用所有的设置、调用绑定事件。
方法 refreshSelects(this)用来将前台select中的元素(如果有的话)copy到插件中。
方法 getCustomData用来返回插件的值
这里的设计思路是:隐藏你在前台写的select元素,返回插件模板中的自定义html串。另外,如果前台select中有option元素,则同步copy到插件中。
三. 绑定插件按钮事件和书写自定义功能函数
//bind events for button
function bindEvents(dlpCustomSelect) {
dlpCustomSelect.elements.addButton.on('click', function() {
addOption(dlpCustomSelect);
});
dlpCustomSelect.elements.deleteButton.on('click', function() {
deleteOption(dlpCustomSelect);
});
//backup method.
/*
$(document).on('click', '.box1 .btn-pull-buttom', function() {
addOption(dlpCustomSelect);
});
*/
}
如以上代码所示,绑定插件的两个按钮的事件。其中addOption和deleteOption函数具体实现这里不再详细阐述。
根据项目实际需要,增加自定义绑定事件和函数。
四. 完成并测试
调用插件:
$(".demo1").dlpcustomSelect({
addButtonText:'Add',
delButtonText:'>>'
});
不更改参数值,直接调用(使用默认参数值):
$(".demo2").dlpcustomSelect();
获取插件中的值:$(".demo1").dlpcustomSelect('getCustomData')
demo下载
总结
本插件的实现大量参考了国外jquery插件制作的通用做法。希望本篇文章能起到抛砖引玉的作用,能引导开发人员自己动手写jquery插件。
本来想挂在github上,但鉴于这个插件的功能很少,就不费周章了。需要的直接在本篇文章下载吧。
另外本人水平有限,如有错误之处还请各位批评指正。
希望本文对你有帮助。