在每次选择后阻止Jquery自动完成选项关闭

时间:2010-10-13 15:09:38

标签: jquery jquery-ui

我正在使用Jquery UI's autocomplete for multiple selectable values。一切都很好,除了每个选择后选项列表关闭。我希望选项保持开放,直到用户决定完成选择。我查看了文档,但我没有看到任何方法来保持选项的开放性。有什么想法吗?

<meta charset="utf-8">

<script>
$(function() {
    var availableTags = [
        "ActionScript",
        "AppleScript",
        "Asp",
        "BASIC",
        "C",
        "C++",
        "Clojure",
        "COBOL",
        "ColdFusion",
        "Erlang",
        "Fortran",
        "Groovy",
        "Haskell",
        "Java",
        "JavaScript",
        "Lisp",
        "Perl",
        "PHP",
        "Python",
        "Ruby",
        "Scala",
        "Scheme"
    ];
    function split( val ) {
        return val.split( /,\s*/ );
    }
    function extractLast( term ) {
        return split( term ).pop();
    }

    $( "#tags" ).autocomplete({
        minLength: 0,
        source: function( request, response ) {
            // delegate back to autocomplete, but extract the last term
            response( $.ui.autocomplete.filter(
                availableTags, extractLast( request.term ) ) );
        },
        focus: function() {
            // prevent value inserted on focus
            return false;
        },
        select: function( event, ui ) {
            var terms = split( this.value );
            // remove the current input
            terms.pop();
            // add the selected item
            terms.push( ui.item.value );
            // add placeholder to get the comma-and-space at the end
            terms.push( "" );
            this.value = terms.join( ", " );
            return false;
        }
    });
});
</script>
    标签编程语言:

16 个答案:

答案 0 :(得分:8)

您可以这样做: 首先定义一个名为readyToClose的变量,并在开头将其设置为false。如果要在下次选择时关闭菜单,请将此变量设置为true。我们还应该重新实现JQuery UI的close方法。

这里我在代码中重新实现了JQuery UI的close方法,而不是在源文件中!这与我们以自定义方式呈现列表的方式相同(例如http://jqueryui.com/demos/autocomplete/custom-data.html

var readyToClose = false;
$( "#tags" ).autocomplete({
    minLength: 0,
    source: function( request, response ) {
        // delegate back to autocomplete, but extract the last term
        response( $.ui.autocomplete.filter(
            availableTags, extractLast( request.term ) ) );
    },
    focus: function() {
        // prevent value inserted on focus
        return false;
    },
    select: function( event, ui ) {
        var terms = split( this.value );
        // remove the current input
        terms.pop();
        // add the selected item
        terms.push( ui.item.value );
        // add placeholder to get the comma-and-space at the end
        terms.push( "" );
        this.value = terms.join( ", " );
        return false;
    }
}).data( "autocomplete" ).close = function(e){
    if(readyToClose)
        clearTimeout(this.closing), this.menu.element.is(":visible") && (this.menu.element.hide(), this.menu.deactivate(), this._trigger("close", e));
    else
        return false;    
};

注意:在较新版本的jQuery(即1.9.0)中,将“autocomplete”替换为“uiAutocomplete”,如:

$("#tags")
    .autocomplete({...})
    .data("uiAutocomplete").close = ...

答案 1 :(得分:4)

我知道这是一个旧问题,可能不再与OP相关,但为了完整起见,更清晰的解决方案是extend自动完成窗口小部件并覆盖{{1} },以及_close extend事件处理程序中的事件对象。这允许您执行自定义逻辑以确定是否应根据具体情况(逐个事件)关闭菜单。另请参阅http://learn.jquery.com/jquery-ui/widget-factory/extending-widgets/

select

在上面的 jQuery.extend(event.originalEvent,{keepOpen:true})中,用于向//override the autocomplete widget jQuery.widget( "ui.autocomplete", jQuery.ui.autocomplete, { _close: function( event ) { if(event!== undefined && event.keepOpen===true) { //trigger new search with current value this.search( null, event ); return true; } //otherwise invoke the original return this._super( event ); } }); $('ac').autocomplete( { ...custom options... select: function( event, ui ) { ...custom logic... if(menu should remain open) { //extend original event with special flag to keep dropdown open //the o-event continues to be passed through the chain of listeners //and will end up being processed during _close() jQuery.extend(event.originalEvent,{keepOpen:true}); //modify value as required jQuery(this).val(...); return false; //prevent selected value from being set, //i.e. keeping modified value above } } } ); 添加特殊的keepOpen属性。由于event.originalEvent修改了原始对象(第一个参数),因此对同一extend的任何后续使用都将具有此属性。阅读并逐步执行代码后,它最终成为event.originalEvent方法中event引用的同一对象。 如果将来这种情况发生变化,这段代码将会中断,但是,维护起来要比18个月前的ingredient_15939建议更容易。

答案 2 :(得分:3)

jQuery UI团队认为这是不良的用户体验:http://forum.jquery.com/topic/enhanced-autocomplete-interest-in-getting-this-into-jqueryui#14737000001125152

所以没有内置的取消方式。您可以尝试在选择&amp; amp;近距离事件。也许您应该缓存结果数组,以便不会发出新请求(如果它处于Ajax模式)。

虽然没有深入了解细节 - 我已经说服我的UI设计师在这个版本中我们不需要这个:)

答案 3 :(得分:3)

我需要同样的能力。 IMO作者可以很容易地给我们选择。我所做的是从UI包中排除自动完成,并包含单独文件的编辑副本:jquery.ui.autocomplete.js(从“development-bundle \ ui”文件夹中获取)。

我添加了几个新选项closeOnSelectupdateElement(两者都是布尔默认值为true),如下所示(位于文件顶部):

$.widget("ui.autocomplete", {
  options: {

  ...

  closeOnSelect: true, // add this line - controls list closing.
  updateElement: true  // add this line - controls updating input box.
},

然后在代码中搜索字符串“ selected:”(只会出现一次)。在此函数中,替换最后几行:

if ( false !== self._trigger( "select", event, { item: item } ) ) {
    self.element.val( item.value );
}
// reset the term after the select event
// this allows custom select handling to work properly
self.term = self.element.val();

self.close( event );
self.selectedItem = item;

有了这个:

if (false !== self._trigger("select", event, { item: item })) {

  if (self.options.updateElement) {
    self.element.val(item.value);
    self.term = self.element.val(); // Ensure term string is same.
  }
  if (self.options.removeOnSelect) { // Remove menu item if option is true.
    console.log(ui);
  } else {
    self.selectedItem = item;
  }
  if (self.options.closeOnSelect) self.close(event); // Close menu if option is true.

} else { // Returned false.
  self.term = self.element.val(); // Ensure term string is same, in case callback changed element value.
}

然后搜索字符串“焦点:功能”(再次,只出现一次)。在该函数中,替换行:

self.element.val(item.value);

有了这个:

if (self.options.updateElement) self.element.val(item.value);

现在,当您创建自动填充时,只需根据需要设置这两个选项:

$("#txtsearch").autocomplete({
  closeOnSelect: false, // Keep list open when item selected.
  updateElement: false, // Don't change the input box contents.
  // etc...

这对我来说很有效。无论原作者是否认为它是“糟糕的UI练习”,每个人的需求都是不同的,选择应该在那里! :)

答案 4 :(得分:1)

您需要编辑jquery-ui javascript文件。在自动填充部分替换

close:function(a){clearTimeout(this.closing);if(this.menu.element.is(":visible")){this._trigger("close",a);this.menu.element.hide();this.menu.deactivate()}}

close:function(a){clearTimeout(this.closing);if(this.menu.element.is(":visible")){if(this._trigger("close",a)!==false){this.menu.element.hide();this.menu.deactivate()}}}

然后您应该能够取消关闭事件,如Shaans的回答中所述。

答案 5 :(得分:1)

不是那么好的解决方案,但我像这样管理它:

var $input = $('input').autocomplete({
    select: function(event, obj) {
        var ac_data = $(event.target).data("autocomplete");
        ac_data.instaSearch = true;

        // Your action

        return false;
    }
});
$input.data("autocomplete")._close = function( event ) {
    if ( this.menu.element.is( ":visible" )) {
        this.menu.element.hide();
        this.menu.blur();
        this.isNewMenu = true;
        this._trigger( "close", event );
        if (this.instaSearch) {
            this.search(this.term);
            this.instaSearch = false;
        }
    }
};

答案 6 :(得分:1)

这个解决方案对我有用。我正在使用自动填充功能显示名称前四个字符可能性的列表。然后,如果用户选择其中一个,那么我有自动完成显示与选择相关联的名称。第一次选择后,自动完成下拉列表保持打开状态,您可以看到列表更改为其他选项。希望这有助于这个主题。

select: function( event, ui ) {
    // the number of chars was driving my decision for behavior, yours may be different
    if (chars.length <= 4) {
        jq.each(ui, function(key, value) {
            jq.each(value, function(key1, value1) {
                // put the selected value in the text area, 
                // since normal behavior will be prevented
                jq("#mod_autocomplete").val(value1);

                // trigger another search
                jq("#mod_autocomplete").autocomplete("search");

                // open the autocomplete dropdown
                jq("#mod_autocomplete").autocomplete("open");
            });

        });

        event.preventDefault();

    } else {
        // put whatever behavior you need here for next selection
    }

}

答案 7 :(得分:1)

我知道那里已经有很多答案,但我仍然提到一个更干净,更直接的答案。请查看此answer

使用与将保持打开菜单的答案相同的代码,现在为了隐藏菜单的外侧点击(在正文上),然后使用下面的代码。

 vendor.bundle.js net::ERR_CONTENT_LENGTH_MISMATCH
bootstrap 195fcde…:54 Uncaught TypeError: Cannot read property 'call' of undefined
    at __webpack_require__ (bootstrap 195fcde…:54)
    at Object.2 (main.ts:11)
    at __webpack_require__ (bootstrap 195fcde…:54)
    at webpackJsonpCallback (bootstrap 195fcde…:25)
    at main.bundle.js:1

答案 8 :(得分:0)

您可以处理自动填充关闭事件http://jqueryui.com/demos/autocomplete/#multiple

代码示例

提供一个回调函数来处理close事件作为init选项。

$( ".selector" ).autocomplete({
   close: function(event, ui) { ... }
});

按类型绑定到关闭事件:autocompleteclose。

$( ".selector" ).bind( "autocompleteclose", function(event, ui) {
  ...
});

答案 9 :(得分:0)

选择:function(event,ui)

if (event.keyCode == 13) {
    document.getElementById("#idofautocomplete").value = document.getElementById("#idofautocomplete").value;
 } else if (event.keyCode == 27) {
        $("#idofautocomplete").autocomplete("close");                                                                                   }

close:function(event,ui)

document.getElementById("idofautocomplete").value = "";

看起来很傻但它对我有用。 用户搜索项目,用回车键选择项目,自动完成仍然打开,用户可以选择更多选项,用户可以随时点击ESC按钮,关闭自动完成,关闭功能从自动完成中清除文本。我从数据库中获取所有信息

答案 10 :(得分:0)

我调整了来自JQuery UI的解决方案以响应close事件并重新打开自动完成菜单,如果输入仍然具有焦点,则没有按下转义来关闭弹出窗口,并且输入的值以任一方式结束a','或''(空格):

close: function(e) {
  if ($el.is(':focus') && e.keyCode !== $.ui.keyCode.ESCAPE && (this.value != null) && /[,\s]+$/.test(this.value)) {
    return $el.autocomplete('search', '');
  }
}

完整的解决方案如下:

var $el = $('#autocomplete-field');
$el.bind('keydown', function(e) {
  if (e.keyCode === $.ui.keyCode.TAB && $(this).data('autocomplete').menu.active) {
    e.preventDefault();
  }
}).autocomplete({
  delay: 0,
  autoFocus: true,
  minLength: 0,
  source: function(req, resp) {
    resp($.ui.autocomplete.filter(source, extractLast(req.term)));
  },
  focus: function() {
    return false;
  },
  select: function(e, ui) {
    var terms;
    terms = regexSplit(this.value);
    terms.pop();
    terms.push(ui.item.value);
    terms.push('');
    this.value = terms.join(', ');
    return false;
  },
  close: function(e) {
    if ($el.is(':focus') && e.keyCode !== $.ui.keyCode.ESCAPE && (this.value != null) && /[,\s]+$/.test(this.value)) {
      return $el.autocomplete('search', '');
    }
  }
}).focus(function() {
  $el.autocomplete('search', '');
});

答案 11 :(得分:0)

速度快,只有css和一点hacky解决方案是:

...
close: function() {
   $('.ui-menu').css('display', 'block'); //or using #ui-id-x where x is number of autocomplete
}
...

答案 12 :(得分:0)

我知道这是旧问题,但它仍然有用,

以下是 jquery UI 的lattest版本的解决方案。

$.ui.autocomplete.prototype._close = function(e){
        return false;    
};

OP可以根据需要修改此代码。

答案 13 :(得分:0)

我自己遇到了这个问题。这是一个简单的衬套,适合所有人使用,只需将其放入自动完成选项即可。

$ac.autocomplete({
  ...
  close: function () { $('.ui-autocomplete').show() }
});

如果要关闭它,或者希望它在关闭时淡出,则可以删除close语句,或者分别在末尾添加.fadeOut()

$ac.autocomplete({
  ...
  close: function () { $('.ui-autocomplete').show().fadeOut() }
});

答案 14 :(得分:0)

我正在集成一个bootstrap jquery ui项目,并且在这里已经完成了打开和关闭的原则。

$.widget('ui.' + internalComplete, $.ui.autocomplete, {
    options: {
        cancelClose: function(event, ui) {
            return false;
        }
    },
    close: function( event ) {
        if (!this._trigger('cancelClose', event, { ui: this.menu.element })) {
            this._super( event );
        }
    }
});

第二步是根据条件选择关闭

        this.uiEl = this.element[internalComplete](
            {
                minLength: 0,
                focus: opts.focus,
                select: opts.select,
                cancelClose: function(event, ui) {
                    var hasMenuItem = _applyOriginalEvent(event, function(e) {
                        return $(e.target).hasClass('ui-menu-item') || null;
                    });
                    return hasMenuItem === true;
                }
            }
        )[internalComplete]('instance');

答案 15 :(得分:0)

/ **  *由高左于2019/12/25创建。  * /

(function($) {

    var internalComplete = 'card-internalAutocomplete';
    var _uuid = 0;

    function _applyOriginalEvent(event, fn) {
        var ret = fn(event);
        if (ret == null) {
            var oe = event.originalEvent;
            if (oe != null) {
                return _applyOriginalEvent(oe, fn);
            }
        }
        return ret;
    }

    $.widget('ui.' + internalComplete, $.ui.autocomplete, {
        options: {
            cancelClose: function(event, ui) {
                return false;
            }
        },
        close: function( event ) {
            if (!this._trigger('cancelClose', event, { ui: this.menu.element })) {
                this._super( event );
            }
        }
    });

    $.widget('ui.card-autocomplete', {
        options: {
            source: null,
            link: ' a.card-link',
            /* source: [
                {
                    title: 'title',
                    subtitle: 'Card subtitle',
                    desc: 'Some quick example text to build on the card title and make up the bulk of the cards content.',
                    'group-prefix': 'js',
                    groups: [
                        {
                            value: "sizzlejs",
                            label: "Sizzle JS",
                            desc: "a pure-JavaScript CSS selector engine",
                            icon: "sizzlejs_32x32.png"
                        }
                    ]
                }
            ],*/
            focus: $.noop,
            select: function(event, ui) {
                return false;
            }
        },
        _create: function() {
            var that = this,
                opts = this.options;

            this.uiEl = this.element[internalComplete](
                {
                    minLength: 0,
                    focus: opts.focus,
                    select: opts.select,
                    cancelClose: function(event, ui) {
                        var hasMenuItem = _applyOriginalEvent(event, function(e) {
                            return $(e.target).hasClass('ui-menu-item') || null;
                        });
                        return hasMenuItem === true;
                    }
                }
            )[internalComplete]('instance');

            this._on(this.element, {
                focus: function(event) {
                    this.uiEl.search(null, event);
                }
            });

            $.extend(this.uiEl, {
                _renderMenu: function(ui, items) {
                    $.each(opts.source, function(i, n) {
                        that._createCards($('<li>').appendTo(ui), n, items);
                    });
                }
            });

            this.uiEl.menu.option('items', this.options.link);
            this._initSource();
        },

        _initSource: function() {
            var data = this.options.source;
            if ($.isArray(data)) {
                this.setSource(this._dataToSource(data));
            }
        },

        _setOption: function(key, value) {
            this._super( key, value );
            if ( key = 'source') {
                this._initSource();
            }
        },

        setSource: function(array) {
            this.uiEl.option('source', array);
        },

        _dataToSource: function(data) {
            var array = [];
            var that = this;
            $.each(data, function(i, n) {
                that._initData(array, n);
            });
            return array;
        },

        _initData: function(array, group) {
            if ($.isArray(group.groups)) {
                var prefix = group.prefix =
                    ( group.prefix || $.ui['card-autocomplete'].getPrefix( true ) );
                $.each(group.groups, function(i, n) {
                    n._prefix = prefix;
                    array.push(n);
                });
                delete group.groups;
            }
        },

        _createCards: function(li, n, list) {
            var body = $('<div>').addClass('card-body');
            li.append( $('<div>').addClass('card').append(body) );
            if (n.title != null) {
                body.append( $('<h5>').addClass('card-title').text(n.title) );
            }
            if (n.subtitle != null) {
                body.append( $('<h6>').addClass('card-subtitle mb-2 text-muted').text(n.subtitle) );
            }
            if (n.desc != null) {
                body.append( $('<p>').addClass('card-text').text(n.desc) );
            }
            this._createList(n, list, body);
        },

        _createList: function(n, list, body) {
            var that = this;
            $.each(list, function(i, li) {
                if (li._prefix == n.prefix) {
                    that._renderLi(li, body);
                }
            });
        },
        _renderLi: function(li, body) {
            $.ui['card-autocomplete'].createLi(li)
                .appendTo( body )
                .data( "ui-autocomplete-item", li );
        }
    });

    $.extend($.ui['card-autocomplete'], {
        createLi: function(li) {
            var link = $('<a>').addClass('card-link').prop('href', '#').text(li.label);
            if (li.desc != null) {
                link.prop('title', li.desc)
            }
            if (li.value != null) {
                link.attr('data-id', li.value);
            }
            return link;
        },
        getPrefix: function(next) {
            if (next) {
                _uuid++;
            }
            return "-" + _uuid;
        }
    });
})(jQuery);

****************************** html **************** *********************************

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>jQuery UI Autocomplete - Custom data and display</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
    <link rel="stylesheet" href="../../../themes/base/jquery.ui.all.css">
    <!--<link rel="stylesheet" href="../../demos.css">-->


    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <script src="jquery-ui.js"></script>
    <script src="jquery.ui.cards.autocomplete.js"></script>
    <!--<script src="../../../ui/jquery.ui.core.js"></script>
    <script src="../../../ui/jquery.ui.widget.js"></script>
    <script src="../../../ui/jquery.ui.position.js"></script>
    <script src="../../../ui/jquery.ui.menu.js"></script>
    <script src="../../../ui/jquery.ui.autocomplete.js"></script>-->
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
    <style>
        #project-label {
            display: block;
            font-weight: bold;
            margin-bottom: 1em;
        }
        #project-icon {
            float: left;
            height: 32px;
            width: 32px;
        }
        #project-description {
            margin: 0;
            padding: 0;
        }
    </style>
    <script>
        $(function() {

            var projects = [
                {
                    title: 'Card title',
                    subtitle: 'Card subtitle',
                    desc: 'Some quick example text to build on the card title and make up the bulk of the cards content.',
                    groups: [
                        {
                            value: "jquery",
                            label: "jQuery",
                            desc: "the write less, do more, JavaScript library",
                            icon: "jquery_32x32.png"
                        },
                        {
                            value: "jquery-ui",
                            label: "jQuery UI",
                            desc: "the official user interface library for jQuery",
                            icon: "jqueryui_32x32.png"
                        }
                    ]
                },
                {
                    title: '第二个分组',
                    subtitle: '第二个分组.....',
                    desc: '第二个分组的描述....',
                    groups: [
                        {
                            value: "sizzlejs",
                            label: "Sizzle JS",
                            desc: "a pure-JavaScript CSS selector engine",
                            icon: "sizzlejs_32x32.png"
                        }
                    ]
                }
            ];

            var ele = $( "#project" )['card-autocomplete']({
                minLength: 0,
                source: projects,
                focus: function( event, ui ) {
                    return false;
                },
                select: function( event, ui ) {
                    return false;
                }
            });


        });
    </script>
</head>
<body>

<div id="project-label">Select a project (type "j" for a start):</div>
<img id="project-icon" src="../images/transparent_1x1.png" class="ui-state-default" alt="">
<input id="project" class="w-50">
<input type="hidden" id="project-id">
<p id="project-description"></p>

<div class="demo-description">
    <p>You can use your own custom data formats and displays by simply overriding the default focus and select actions.</p>
    <p>Try typing "j" to get a list of projects or just press the down arrow.</p>
</div>










</body>
</html>