在项目中需要一个添加标签的小插件,查看了一些已有插件后,发现很现成的高级插件,也有比较简单的插件。最后还是决定自己来写,这样能控制代码,以后与其他插件结合使用的时候能更好的把控。初步在IE6 7 8,firefox,chrome中做了测试,可以通过。
我是仿照163邮箱中添加邮箱的方式写的,插件如下:
一、制作静态效果
先用html和css做出个样子出来,然后再根据样式来做动态效果。
<h2 style="padding:10px">静态效果</h2> <label class="jtag_error">5424@qq</label> <label class="jtag_error jtag_error_hover">5dasdasdasdq</label> <label class="jtag_error jtag_error_active">5dasdasq</label> <label class="jtag_correct">pwstrick@163.com</label> <label class="jtag_correct_hover jtag_correct">peach@163.com</label> <label class="jtag_correct_active jtag_correct">peach@163.com</label> <input type="text" class="jtag_input"/><span style="color:#666;font-size:.75em">多个标签请用分号分开,双击已完成标签可做修改</span>
标签有三种样式,依次是普通样式,第二种是:hover移上去的样式,第三种是选中的样式;两种状态的标签,一种是符合规则的标签,另外一种是不符合要求的标签,上面的规则是邮箱格式。
.jtag_correct{ border:1px solid #c2d4e3;padding:5px 8px;font-size:.75em;background:#e8f0f7; color:#7f7f7f;margin-right:5px;cursor:pointer;display:inline-block;vertical-align:middle } .jtag_correct_hover{border-color:#cbdeee;color:#6d92b8;background:#cbdeee} .jtag_correct_active{border-color:#2d59b3;color:#fff;background:#0f6099} .jtag_error{ border:1px solid #edb8b8;padding:5px 8px;font-size:.75em;background:#ffeaea; color:#c30;margin-right:5px;cursor:pointer;display:inline-block;vertical-align:middle } .jtag_error_hover{border-color:#e0aaaa;color:#b10b2c;background:#fddcdc} .jtag_error_active{border-color:#dc9c9c;color:#fff;background:#ee4d4d} .jtag_input{border:0;vertical-align:middle;width:50px}
1、标签我就用label表示,设置成display:inline-block,这样控制对齐比较方便,宽度未定,用padding把整个块给撑开,字体大小用了em单位
2、标签与输入框之间的上下对齐用vertical-align:middle解决
3、为了简单一点,我将input输入框与label标签设为兄弟关系
4、correct是指符合规则的样式,error是指不符合规则的样式,hover是移到标签上的样式,active是选中标签的样式
5、把输入框隐藏掉
二、插件格式
;(function (factory) { 'use strict'; // Register as an AMD module, compatible with script loaders like RequireJS. if (typeof define === 'function' && define.amd) { define(['jquery'], factory); } else { factory(jQuery); } }(function ($, undefined) { 'use strict'; $.fn.jtag = function (method) { }; }));
1、第一个分号是为了防止在与其他代码压缩到一起的时候合在一行中,这样会出现语法错误。例如var i=0(function(factory){......}(..);
2、'use strict'是开启严格模式,使Javascript解释器可以用"严格"的语法来解析代码,以帮助开发人员发现错误
3、如果使用了requirejs模块载入框架,define(['jquery'], factory)这句就是让插件支持AMD规范
4、function ($, undefined) 这里面的undefined是为了防止在引入其他js文件的时候,使用被重写了的undefined
三、设置常量
var keys = { BACKSPACE: 8, DELETE: 46, DOWN: 40, END: 35, ENTER: 13, HOME: 36, LEFT: 37, RIGHT: 39, UP: 38, SEMICOLON1: 186, SEMICOLON2: 59 };var defaults = { inputCss : 'jtag_input', tagErrorCss : 'jtag_error', tagErrorHoverCss : 'jtag_error_hover', tagErrorActiveCss : 'jtag_error_active', tagCorrectCss : 'jtag_correct', tagCorrectHoverCss : 'jtag_correct_hover', tagCorrectActiveCss : 'jtag_correct_active', regexInput : '' };
1、keys里面定义了键盘的码,SEMICOLON1和SEMICOLON2是指分号,firefox,IE对于分号的码有点不同
2、defaults是插件的默认参数
inputCss |
输入框样式 |
tagErrorCss |
标签不符合规则样式 |
tagErrorHoverCss |
标签不符合规则移上去样式 |
tagErrorActiveCss |
标签不符合规则选中样式 |
tagCorrectCss |
标签符合规则样式 |
tagCorrectHoverCss |
标签符合规则移上去样式 |
tagCorrectActiveCss |
标签符合规则选中样式 |
regexInput |
标签的正则验证规则 |
四、初始化
/** * @method private * @name _init * @description Initializes plugin * @param opts [object] "Initialization options" */ function _init(opts) { opts = $.extend({}, defaults, opts || {}); // Apply to each element var $items = $(this); for (var i = 0, count = $items.length; i < count; i++) { _build($items.eq(i), opts); } return $items; } /** * @method private * @name _build * @description Builds each instance * @param $node [jQuery object] "目标对象" * @param opts [object] "插件参数" */ function _build($node, opts) { $node.addClass(opts.inputCss); // Store plugin data var data = $.extend({ $node: $node }, opts); data.$node.on("keyup.jtag", data, _onKeyup) .on("keydown.jtag", data, _onKeydown) .on("blur.jtag", data, _onBlur); } $.fn.jtag = function () { return _init.apply(this, arguments); };
1、_init是做初始化操作,先是将传入的插件参数与默认的参数合并起来,然后做创建
2、$('.class').jtag()的时候会匹配多个元素,_init中有个for循环,用于将这多个元素创建插件
3、插件定义的时候将_init函数apply一下,这样可以将函数中的上下文this设置为插件对象
4、_build中先给输入框一个样式,然后将输入框对象与插件参数合并,传入到下面_onKeyup几个方法中
5、使用on方法,在选择元素上绑定事件,keyup.jtag分别是事件类型与命名空间,第二个参数data会作为event的一个属性传递给事件处理函数
五、输入框事件
输入框绑定了三种事件,keyup、keydown和blur
1)keyup是在输入框中当按钮被松开时发生的事件
1.只要输入分号(;)就将输入框中的内容添加为标签,如果输入框中没有内容就不会创建标签
2.按键盘中的上与左建能够选中前面的标签,前置条件是焦点必须是在输入框中
3.按键盘中的下与右建能够选中后面的标签,前置条件是焦点必须是在输入框中
4.输入框中只要有内容,所有标签的选中样式就要移除
/** * @method private * @name _onKeyup * @description 按下某个按钮后发生的事件,输入框中内容已改变 * @param e [jQuery object] "Event data" */ function _onKeyup(e) { var data = e.data; var code = _getKeyCode(e); var $input = data.$node; //当输入框中有内容时候要移除选中标记 if($input.val().length > 0) _removeSelected($input, data); switch(code) { //case keys.ENTER: case keys.SEMICOLON1: case keys.SEMICOLON2: _addTag($input, data);//输入法直接回车就增加了 break; case keys.LEFT: case keys.UP: _selectPrev($input, data); break; case keys.RIGHT: case keys.DOWN: _selectNext($input, data); break; } }
2)keydown是当键盘或按钮被按下时的事件
这个事件是为了删除选定标签而设置的,原先是放在keyup中,但是有个问题,就是当用backspace键删除输入框中的内容的时候,输入到最后一个字符的时候会同时把输入框旁边的标签也一并删除掉。后面就改用了keydown事件。
/** * @method private * @name _onKeydown * @description 按下某个按钮后发生的事件,输入框中内容未改变 * @param e [jQuery object] "Event data" */ function _onKeydown(e) { var data = e.data; var code =_getKeyCode(e); switch(code) { case keys.BACKSPACE: case keys.DELETE: _delSelectedTag(data); break; } }
3)blur是当元素失去焦点时触发时的事件
输入框失去焦点后,将内容直接设置为标签,并将所有标签的选中状态移除。
/** * @method private * @name _onBlur * @description 失去焦点的时候 * @param e [jQuery object] "Event data" */ function _onBlur(e) { var opts = e.data; _removeSelected(opts.$node, opts); _addTag(opts.$node, opts); }
六、标签内容规则验证与过滤
在参数中有一个参数是正则,用于验证的,这个规则将会在好几个地方用到,所以我放到了一个私有函数中。没几行,不过里面有一个坑,就是正则的test方法,一开始不理解它,后面就发生了奇怪的问题,就是这个规则验证同一个内容,有时候是通过的有时候却不能通过,最后搜索后,发现test会改变lastIndex的值,导致有时候对有时候不对。
/** * @method private * @name _validate * @description 根据正则表达式检验标签格式 * @param value [string] "标签值" * @param opts [object] "插件参数" */ function _validate(value, opts) { if(opts.regexInput) { opts.regexInput.lastIndex=0;//test会修改lastIndex的值 return opts.regexInput.test(value); } return true; }
输入框中输入分号也会生成标签,这个分号可以是英文输入法的也可是中文输入法的,在生成标签的时候,我会过滤掉分号,这里用了replace,这地方也有一个坑,就是replace中不用正则的过滤规则,只是用字符串来做,只会把第一个匹配的过滤掉,后面的还是会存在,例如replace(';', '')。
/** * @method private * @name _filter * @description 过滤输入值 * @param value [string] "标签内容" */ function _filter(value) { return value.replace(/[;;]/g, '');//replace要替换所有的需要用正则 }
七、生成标签
生成的标签将会给它绑定hover、click和dblclick事件
标签click后,会添加选中样式,并将焦点设置为输入框,这样才能使用键盘中的上下左右键做操作
标签dblclick后,即双击,会将标签变成一个输入框,给这个输入框绑定了keyup与blur事件,keyup实现输入框长度自适应,blur后将再次生成标签
/** * @method private * @name _generateTag * @description 新增一个正确或错误的标签 * @param value [string] "标签内容已过滤空白" * @param opts [object] "插件参数" * @param $input [jQuery object] "定义为插件的输入框对象,不能为标签双击后生成的输入框" * @return $tag [object] 动态生成的标签 */ function _generateTag(value, opts, $input) { var $tag, css, hover, active; if(_validate(value, opts)) {//验证或无验证 css = opts.tagCorrectCss; hover = opts.tagCorrectHoverCss; active = opts.tagCorrectActiveCss; }else { css = opts.tagErrorCss; hover = opts.tagErrorHoverCss; active = opts.tagErrorActiveCss; } var $tag = $('<label class="'+css+'">'+value+'</label>').bind('hover', function(e) { e.type == 'mouseenter' ? $(this).addClass(hover) : $(this).removeClass(hover); }).bind('click', function() { _removeSelected($input, opts);//移除兄弟标签选中状态 $(this).addClass(active);//选中class添加 //输入框中有内容的就直接添加为标签 _addTag($input, opts); $input.focus();//将输入框为选中状态,不选中按backspace会回退网页 }).bind('dblclick', function() {//双击变为修改状态 var text = $(this).text(); var $text = $('<input type="text"/>').css({border:0, marginRight:5}).attr('size', text.length+2).val(text).bind('blur', function() { _addTag($(this), opts); $(this).remove(); $input.focus(); }).bind('keyup', function() {//输入框宽度自适应 $(this).attr("size", $(this).val().length + 5); }); $(this).before($text); $(this).remove(); $text.focus(); }); return $tag; }
好了,就这么多了,代码比上一个插件可编辑的下拉框多了6倍多,这个插件里还有很多BUG存在,大家可以自己来改,如果发现了BUG的的话,就请告诉我一下啊^ ^