博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
改造jQuery-Tagit 插件支持中文全角的逗号和空格
阅读量:5214 次
发布时间:2019-06-14

本文共 24019 字,大约阅读时间需要 80 分钟。

jQuery 的 tagit 插件效果还是不错的,今天用到该插件但发现不能自定义标签分隔符,只能是英文半角逗号或空格,于是想改造下

效果:

先研究了一番插件的代码,发现并不能通过插件自身的扩展方法来实现,

标签输入框是插件自己生成的,所以本来想在外部绑定 keydown 事件但由于事件绑定先后顺序的问题不能实现,只能修改代码了:

改动不多,主要是增加了三个事件绑定在插件原来的 keydown 事件之前绑定一个自定义的 keydown 以及 blur 事件处理标签内容的过滤,以及 keyup 后模拟键盘的半角逗号按键回调触发原来的 keydown 事件,从而不会太多改动插件代码,不妨碍以后升级

这里还有一个问题,涉及到 keydown、keypress、keyup 顺序和效率的问题,简单说说:keydown是指键盘开始往下按下,此时 input 控件并没有得到最终的按键字符,只有一个响应事件 keyCode,而且此时如果是用的中文输入法则 keyCode 始终都是 229,也就不能通过该事件来判断全角中文的输入字符了,但这个效率应该是很高的,处理效果会很流畅,keypress 也是差不多的

代码:

/** jQuery UI Tag-it!** @version v2.0 (06/2011)** Copyright 2011, Levy Carneiro Jr.* Released under the MIT license.* http://aehlke.github.com/tag-it/LICENSE** Homepage:*   http://aehlke.github.com/tag-it/** Authors:*   Levy Carneiro Jr.*   Martin Rehfeld*   Tobias Schmidt*   Skylar Challand*   Alex Ehlke** Maintainer:*   Alex Ehlke - Twitter: @aehlke** Dependencies:*   jQuery v1.4+*   jQuery UI v1.8+*/(function($) {    $.widget('ui.tagit', {        options: {            allowDuplicates   : false,            caseSensitive     : true,            fieldName         : 'tags',            placeholderText   : null,   // Sets `placeholder` attr on input field.            readOnly          : false,  // Disables editing.            removeConfirmation: false,  // Require confirmation to remove tags.            tagLimit          : null,   // Max number of tags allowed (null for unlimited).            // Used for autocomplete, unless you override `autocomplete.source`.            availableTags     : [],            // Use to override or add any options to the autocomplete widget.            //            // By default, autocomplete.source will map to availableTags,            // unless overridden.            autocomplete: {},            // Shows autocomplete before the user even types anything.            showAutocompleteOnFocus: false,            // When enabled, quotes are unneccesary for inputting multi-word tags.            allowSpaces: false,            // The below options are for using a single field instead of several            // for our form values.            //            // When enabled, will use a single hidden field for the form,            // rather than one per tag. It will delimit tags in the field            // with singleFieldDelimiter.            //            // The easiest way to use singleField is to just instantiate tag-it            // on an INPUT element, in which case singleField is automatically            // set to true, and singleFieldNode is set to that element. This            // way, you don't need to fiddle with these options.            singleField: false,            // This is just used when preloading data from the field, and for            // populating the field with delimited tags as the user adds them.            singleFieldDelimiter: ',',            // Set this to an input DOM node to use an existing form field.            // Any text in it will be erased on init. But it will be            // populated with the text of tags as they are created,            // delimited by singleFieldDelimiter.            //            // If this is not set, we create an input node for it,            // with the name given in settings.fieldName.            singleFieldNode: null,            // Whether to animate tag removals or not.            animate: true,            // Optionally set a tabindex attribute on the input that gets            // created for tag-it.            tabIndex: null,            // Event callbacks.            beforeTagAdded      : null,            afterTagAdded       : null,            beforeTagRemoved    : null,            afterTagRemoved     : null,            onTagClicked        : null,            onTagLimitExceeded  : null,            // DEPRECATED:            //            // /!\ These event callbacks are deprecated and WILL BE REMOVED at some            // point in the future. They're here for backwards-compatibility.            // Use the above before/after event callbacks instead.            onTagAdded  : null,            onTagRemoved: null,            // `autocomplete.source` is the replacement for tagSource.            tagSource: null            // Do not use the above deprecated options.        },        _create: function() {            // for handling static scoping inside callbacks            var that = this;            // There are 2 kinds of DOM nodes this widget can be instantiated on:            //     1. UL, OL, or some element containing either of these.            //     2. INPUT, in which case 'singleField' is overridden to true,            //        a UL is created and the INPUT is hidden.            if (this.element.is('input')) {                this.tagList = $('
    ').insertAfter(this.element); this.options.singleField = true; this.options.singleFieldNode = this.element; this.element.addClass('tagit-hidden-field'); } else { this.tagList = this.element.find('ul, ol').andSelf().last(); } this.tagInput = $('').addClass('ui-widget-content'); if (this.options.readOnly) this.tagInput.attr('disabled', 'disabled'); if (this.options.tabIndex) { this.tagInput.attr('tabindex', this.options.tabIndex); } if (this.options.placeholderText) { this.tagInput.attr('placeholder', this.options.placeholderText); } if (!this.options.autocomplete.source) { this.options.autocomplete.source = function(search, showChoices) { var filter = search.term.toLowerCase(); var choices = $.grep(this.options.availableTags, function(element) { // Only match autocomplete options that begin with the search term. // (Case insensitive.) return (element.toLowerCase().indexOf(filter) === 0); }); if (!this.options.allowDuplicates) { choices = this._subtractArray(choices, this.assignedTags()); } showChoices(choices); }; } if (this.options.showAutocompleteOnFocus) { this.tagInput.focus(function(event, ui) { that._showAutocomplete(); }); if (typeof this.options.autocomplete.minLength === 'undefined') { this.options.autocomplete.minLength = 0; } } // Bind autocomplete.source callback functions to this context. if ($.isFunction(this.options.autocomplete.source)) { this.options.autocomplete.source = $.proxy(this.options.autocomplete.source, this); } // DEPRECATED. if ($.isFunction(this.options.tagSource)) { this.options.tagSource = $.proxy(this.options.tagSource, this); } this.tagList .addClass('tagit') .addClass('ui-widget ui-widget-content ui-corner-all') // Create the input field. .append($('
  • ').append(this.tagInput)) .click(function(e) { var target = $(e.target); if (target.hasClass('tagit-label')) { var tag = target.closest('.tagit-choice'); if (!tag.hasClass('removed')) { that._trigger('onTagClicked', e, {tag: tag, tagLabel: that.tagLabel(tag)}); } } else { // Sets the focus() to the input field, if the user // clicks anywhere inside the UL. This is needed // because the input field needs to be of a small size. that.tagInput.focus(); } }); // Single field support. var addedExistingFromSingleFieldNode = false; if (this.options.singleField) { if (this.options.singleFieldNode) { // Add existing tags from the input field. var node = $(this.options.singleFieldNode); var tags = node.val().split(this.options.singleFieldDelimiter); node.val(''); $.each(tags, function(index, tag) { that.createTag(tag, null, true); addedExistingFromSingleFieldNode = true; }); } else { // Create our single field input after our list. this.options.singleFieldNode = $(''); this.tagList.after(this.options.singleFieldNode); } } // Add existing tags from the list, if any. if (!addedExistingFromSingleFieldNode) { this.tagList.children('li').each(function() { if (!$(this).hasClass('tagit-new')) { that.createTag($(this).text(), $(this).attr('class'), true); $(this).remove(); } }); } // 增加键盘输入事件,支持全角下的(,)逗号和( )空格 // 注意顺序必须在插件绑定前绑定自定义事件 this.tagInput.on('keydown blur', function(){ var val = that.tagInput.val(); val = val.replace(/(,| )+/g, ''); that.tagInput.val(val); }).keyup(function() { var val = $.trim(that.tagInput.val()); var lastChar = val ? val.substr(-1, 1) : ''; if (lastChar == ',' || lastChar == ' ') { that.tagInput.trigger('keydown', { keyCode:$.ui.keyCode.COMMA }); } }); // Events. this.tagInput .keydown(function(event, opt) { // 增加外部模拟逗号键盘输入事件 if (typeof opt == 'object' && typeof opt.keyCode == 'number') { event.which = opt.keyCode; event.shiftKey = false; } // Backspace is not detected within a keypress, so it must use keydown. if (event.which == $.ui.keyCode.BACKSPACE && that.tagInput.val() === '') { var tag = that._lastTag(); if (!that.options.removeConfirmation || tag.hasClass('remove')) { // When backspace is pressed, the last tag is deleted. that.removeTag(tag); } else if (that.options.removeConfirmation) { tag.addClass('remove ui-state-highlight'); } } else if (that.options.removeConfirmation) { that._lastTag().removeClass('remove ui-state-highlight'); } // Comma/Space/Enter are all valid delimiters for new tags, // except when there is an open quote or if setting allowSpaces = true. // Tab will also create a tag, unless the tag input is empty, // in which case it isn't caught. if ( (event.which === $.ui.keyCode.COMMA && event.shiftKey === false) || event.which === $.ui.keyCode.ENTER || ( event.which == $.ui.keyCode.TAB && that.tagInput.val() !== '' ) || ( event.which == $.ui.keyCode.SPACE && that.options.allowSpaces !== true && ( $.trim(that.tagInput.val()).replace( /^s*/, '' ).charAt(0) != '"' || ( $.trim(that.tagInput.val()).charAt(0) == '"' && $.trim(that.tagInput.val()).charAt($.trim(that.tagInput.val()).length - 1) == '"' && $.trim(that.tagInput.val()).length - 1 !== 0 ) ) ) ) { // Enter submits the form if there's no text in the input. if (!(event.which === $.ui.keyCode.ENTER && that.tagInput.val() === '')) { event.preventDefault(); } // Autocomplete will create its own tag from a selection and close automatically. if (!(that.options.autocomplete.autoFocus && that.tagInput.data('autocomplete-open'))) { that.tagInput.autocomplete('close'); that.createTag(that._cleanedInput()); } } }).blur(function(e){ // Create a tag when the element loses focus. // If autocomplete is enabled and suggestion was clicked, don't add it. if (!that.tagInput.data('autocomplete-open')) { that.createTag(that._cleanedInput()); } }); // Autocomplete. if (this.options.availableTags || this.options.tagSource || this.options.autocomplete.source) { var autocompleteOptions = { select: function(event, ui) { that.createTag(ui.item.value); // Preventing the tag input to be updated with the chosen value. return false; } }; $.extend(autocompleteOptions, this.options.autocomplete); // tagSource is deprecated, but takes precedence here since autocomplete.source is set by default, // while tagSource is left null by default. autocompleteOptions.source = this.options.tagSource || autocompleteOptions.source; this.tagInput.autocomplete(autocompleteOptions).bind('autocompleteopen.tagit', function(event, ui) { that.tagInput.data('autocomplete-open', true); }).bind('autocompleteclose.tagit', function(event, ui) { that.tagInput.data('autocomplete-open', false); }); this.tagInput.autocomplete('widget').addClass('tagit-autocomplete'); } }, destroy: function() { $.Widget.prototype.destroy.call(this); this.element.unbind('.tagit'); this.tagList.unbind('.tagit'); this.tagInput.removeData('autocomplete-open'); this.tagList.removeClass([ 'tagit', 'ui-widget', 'ui-widget-content', 'ui-corner-all', 'tagit-hidden-field' ].join(' ')); if (this.element.is('input')) { this.element.removeClass('tagit-hidden-field'); this.tagList.remove(); } else { this.element.children('li').each(function() { if ($(this).hasClass('tagit-new')) { $(this).remove(); } else { $(this).removeClass([ 'tagit-choice', 'ui-widget-content', 'ui-state-default', 'ui-state-highlight', 'ui-corner-all', 'remove', 'tagit-choice-editable', 'tagit-choice-read-only' ].join(' ')); $(this).text($(this).children('.tagit-label').text()); } }); if (this.singleFieldNode) { this.singleFieldNode.remove(); } } return this; }, _cleanedInput: function() { // Returns the contents of the tag input, cleaned and ready to be passed to createTag return $.trim(this.tagInput.val().replace(/^"(.*)"$/, '$1')); }, _lastTag: function() { return this.tagList.find('.tagit-choice:last:not(.removed)'); }, _tags: function() { return this.tagList.find('.tagit-choice:not(.removed)'); }, assignedTags: function() { // Returns an array of tag string values var that = this; var tags = []; if (this.options.singleField) { tags = $(this.options.singleFieldNode).val().split(this.options.singleFieldDelimiter); if (tags[0] === '') { tags = []; } } else { this._tags().each(function() { tags.push(that.tagLabel(this)); }); } return tags; }, _updateSingleTagsField: function(tags) { // Takes a list of tag string values, updates this.options.singleFieldNode.val to the tags delimited by this.options.singleFieldDelimiter $(this.options.singleFieldNode).val(tags.join(this.options.singleFieldDelimiter)).trigger('change'); }, _subtractArray: function(a1, a2) { var result = []; for (var i = 0; i < a1.length; i++) { if ($.inArray(a1[i], a2) == -1) { result.push(a1[i]); } } return result; }, tagLabel: function(tag) { // Returns the tag's string label. if (this.options.singleField) { return $(tag).find('.tagit-label:first').text(); } else { return $(tag).find('input:first').val(); } }, _showAutocomplete: function() { this.tagInput.autocomplete('search', ''); }, _findTagByLabel: function(name) { var that = this; var tag = null; this._tags().each(function(i) { if (that._formatStr(name) == that._formatStr(that.tagLabel(this))) { tag = $(this); return false; } }); return tag; }, _isNew: function(name) { return !this._findTagByLabel(name); }, _formatStr: function(str) { if (this.options.caseSensitive) { return str; } return $.trim(str.toLowerCase()); }, _effectExists: function(name) { return Boolean($.effects && ($.effects[name] || ($.effects.effect && $.effects.effect[name]))); }, createTag: function(value, additionalClass, duringInitialization) { var that = this; value = $.trim(value); if(this.options.preprocessTag) { value = this.options.preprocessTag(value); } if (value === '') { return false; } if (!this.options.allowDuplicates && !this._isNew(value)) { var existingTag = this._findTagByLabel(value); if (this._trigger('onTagExists', null, { existingTag: existingTag, duringInitialization: duringInitialization }) !== false) { if (this._effectExists('highlight')) { existingTag.effect('highlight'); } } return false; } if (this.options.tagLimit && this._tags().length >= this.options.tagLimit) { this._trigger('onTagLimitExceeded', null, {duringInitialization: duringInitialization}); return false; } var label = $(this.options.onTagClicked ? '' : '').text(value); // Create tag. var tag = $('
  • ') .addClass('tagit-choice ui-widget-content ui-state-default ui-corner-all') .addClass(additionalClass) .append(label); if (this.options.readOnly){ tag.addClass('tagit-choice-read-only'); } else { tag.addClass('tagit-choice-editable'); // Button for removing the tag. var removeTagIcon = $('') .addClass('ui-icon ui-icon-close'); var removeTag = $('\xd7') // \xd7 is an X .addClass('tagit-close') .append(removeTagIcon) .click(function(e) { // Removes a tag when the little 'x' is clicked. that.removeTag(tag); }); tag.append(removeTag); } // Unless options.singleField is set, each tag has a hidden input field inline. if (!this.options.singleField) { var escapedValue = label.html(); tag.append(''); } if (this._trigger('beforeTagAdded', null, { tag: tag, tagLabel: this.tagLabel(tag), duringInitialization: duringInitialization }) === false) { return; } if (this.options.singleField) { var tags = this.assignedTags(); tags.push(value); this._updateSingleTagsField(tags); } // DEPRECATED. this._trigger('onTagAdded', null, tag); this.tagInput.val(''); // Insert tag. this.tagInput.parent().before(tag); this._trigger('afterTagAdded', null, { tag: tag, tagLabel: this.tagLabel(tag), duringInitialization: duringInitialization }); if (this.options.showAutocompleteOnFocus && !duringInitialization) { setTimeout(function () { that._showAutocomplete(); }, 0); } }, removeTag: function(tag, animate) { animate = typeof animate === 'undefined' ? this.options.animate : animate; tag = $(tag); // DEPRECATED. this._trigger('onTagRemoved', null, tag); if (this._trigger('beforeTagRemoved', null, {tag: tag, tagLabel: this.tagLabel(tag)}) === false) { return; } if (this.options.singleField) { var tags = this.assignedTags(); var removedTagLabel = this.tagLabel(tag); tags = $.grep(tags, function(el){ return el != removedTagLabel; }); this._updateSingleTagsField(tags); } if (animate) { tag.addClass('removed'); // Excludes this tag from _tags. var hide_args = this._effectExists('blind') ? ['blind', {direction: 'horizontal'}, 'fast'] : ['fast']; var thisTag = this; hide_args.push(function() { tag.remove(); thisTag._trigger('afterTagRemoved', null, {tag: tag, tagLabel: thisTag.tagLabel(tag)}); }); tag.fadeOut('fast').hide.apply(tag, hide_args).dequeue(); } else { tag.remove(); this._trigger('afterTagRemoved', null, {tag: tag, tagLabel: this.tagLabel(tag)}); } }, removeTagByLabel: function(tagLabel, animate) { var toRemove = this._findTagByLabel(tagLabel); if (!toRemove) { throw "No such tag exists with the name '" + tagLabel + "'"; } this.removeTag(toRemove, animate); }, removeAll: function() { // Removes all tags. var that = this; this._tags().each(function(index, tag) { that.removeTag(tag, false); }); } });})(jQuery);

    使用方法很简单的就不多说了,下面是相关网址介绍:

    需要配置 jQuery-ui 一起使用的:

    效果:

    转载于:https://www.cnblogs.com/zhouzme/p/5758423.html

    你可能感兴趣的文章
    中国剩余定理
    查看>>
    基础笔记一
    查看>>
    uva 10137 The trip
    查看>>
    spring 解决中文乱码问题
    查看>>
    hdu 4268
    查看>>
    启动tomcat时cmd窗口一闪而过
    查看>>
    两个有序数列,求中间值 Median of Two Sorted Arrays
    查看>>
    vue路由的实现原理
    查看>>
    Java核心技术:Java异常处理
    查看>>
    Python 学习笔记一
    查看>>
    引入列表,将对话分类添加到对应列表中
    查看>>
    回文子串
    查看>>
    Count Numbers
    查看>>
    React——JSX
    查看>>
    编写高质量代码改善C#程序的157个建议——建议110:用类来代替enum
    查看>>
    最大公约数求解
    查看>>
    网卡bond技术
    查看>>
    UITabbarController的UITabbarItem(例:"我的")点击时,判断是否登录
    查看>>
    机器学习之支持向量机(一):支持向量机的公式推导
    查看>>
    对【SQL SERVER 分布式事务解决方案】的心得补充
    查看>>