每个单词下方的jQuery-ui自动完成下拉菜单

Posted

技术标签:

【中文标题】每个单词下方的jQuery-ui自动完成下拉菜单【英文标题】:Jquery-ui autocomplete dropdown below each word 【发布时间】:2013-01-18 07:23:36 【问题描述】:

我正在使用来自 jquery-ui 的Autocomplete。在多个值中,您可以在空格后获取每个单词的下拉列表,但下拉列表显示为输入框的大小。是否可以使下拉菜单出现在每个光标下方,其宽度等于下拉单词而不是输入框的整个长度?

编辑:示例(类似 Google 的搜索框):

当您访问谷歌并随着句子的继续输入一个长句子时,在每个单词之后,光标位置下方的每个单词都会出现一个自动完成下拉菜单。所以我需要一个类似的下拉菜单,可以添加到 jQuery Autocomplete

我的功能这么大是因为它具有多个单词和按字母顺序显示数组的功能。 这是<script 代码:

    <script>

    $(function() 

            var availableTags = <?php echo json_encode($sometags); ?>;


            function split( val ) 
            return val.split( / \s*/ );
            
            function extractLast( term ) 
            return split( term ).pop();
            
             $( "#query" )
            // don't navigate away from the field on tab when selecting an item
            .bind( "keydown", function( event ) 
            if ( event.keyCode === $.ui.keyCode.TAB &&
            $( this ).data( "autocomplete" ).menu.active ) 
            event.preventDefault();
            
            )
            .autocomplete(
            minLength: 1,
            source: function( request, response ) 
            // delegate back to autocomplete, but extract the last term
            response( $.ui.autocomplete.filter(
            availableTags, extractLast( request.term ) ) );
            var term = $.ui.autocomplete.escapeRegex(request.term)
            , startsWithMatcher = new RegExp("^" + term, "i")
            , startsWith = $.grep(availableTags, function(value) 
                return startsWithMatcher.test(value.label || value.value || value);
            )
            , containsMatcher = new RegExp(term, "i")
            , contains = $.grep(availableTags, function (value) 
                return $.inArray(value, startsWith) < 0 &&
                    containsMatcher.test(value.label || value.value || value);
            );

            response(startsWith.concat(contains));
            ,
            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>

编辑:就像谷歌框一样,下拉列表应该包含在输入框的宽度内,例如,输入框中最后一个单词的下拉框应该调整大小而不是向右而是向左。下拉框的右边缘应与输入框的右边缘对齐,并且下拉框的总宽度(如果单词与输入框一样大或大于输入框)不应超过输入框的宽度。

更新: 这是类似 google 的自动完成功能的最终模式:Fiddle(2013 年 16 月 2 日更新) 特点: 1) 修复了多个单词按字母顺序排列的建议 (jQuery-ui autocomplete multiple values sort results alphabetically) 2) 从 2 个数组中检索建议,第一个建议以输入框的全宽打开,如谷歌,其余建议以最长建议的宽度打开 3) 修复了“空格”(多个值)后输入单词之前下拉菜单打开的错误。 4) 防止下拉菜单在末尾保持打开状态,同时在其间添加单词。 5) 16/2/2013更新:当从建议框输入的标签长度​​超过输入框的长度,输入下一个标签时,输入框从第一个标签开始显示标签或移回第一个标签位置,不是从这里看到的光标最后放置的位置 - http://jqueryui.com/autocomplete/#multiple。此问题已修复。

这是一个类似的小提琴,只使用了一个数组,并且建议总是在最长建议的宽度 - FIDDLE

感谢 Jeffery To 提供了该问题的主要解决方案,感谢 Vadim 和 Dom,他们的回答为创建上述 mod 提供了有用的想法。

【问题讨论】:

当您进入谷歌并随着句子的继续输入一个长句子时,在每个单词之后,光标位置下方的每个单词都会出现一个自动完成下拉菜单。所以我需要一个类似的下拉列表,可以添加到 jQuery Autocomplete 这样的? jsfiddle.net/ExplosionPIlls/9DbTw @Explosion Pills - 这是一个简单的自动完成功能,我需要的是一个类似于 google 的功能(请参阅上面的评论),对我来说,即使是第二个单词的意思也会出现一个新的下拉列表,在你的小提琴中,如果我输入“word”,列表就会出现,但如果我在第一个“word”之后再次输入“word”,至少相同的下拉菜单也不会出现。 @Javier***lyn 你为什么要输入word word?您的列表的来源是哪里? @Explosion Pills - 因为在您的小提琴中,所有数组都以单词开头。所以检查一个新词是否会出现一个新的下拉列表的唯一方法就是输入“word word”。在我的 js 中,我有一个数组,其中包含许多没有间隔的单词,如果我输入“hello world”,当我按下“h”时,我会得到一个包含以“h”开头的单词的下拉列表,以及一个包含以“开头的单词”的下拉列表w' 当我按'w'时。但是我的下拉列表中每个单词的框长度相同,但我希望框仅出现在光标下方和单词大小处。 【参考方案1】:

正如其他答案所建议的那样,我们测量输入字段中文本的宽度(使用隐藏元素),然后偏移自动完成框。我们还可以重置自动完成框的宽度,使其仅与最长建议一样宽 (demo):

open: function( event, ui ) 
    var input = $( event.target ),
        widget = input.autocomplete( "widget" ),
        style = $.extend( input.css( [
            "font",
            "border-left",
            "padding-left"
        ] ), 
            position: "absolute",
            visibility: "hidden",
            "padding-right": 0,
            "border-right": 0,
            "white-space": "pre"
         ),
        div = $( "<div/>" ),
        pos = 
            my: "left top",
            collision: "none"
        ,
        offset = -7; // magic number to align the first letter
                     // in the text field with the first letter
                     // of suggestions
                     // depends on how you style the autocomplete box

    widget.css( "width", "" );

    div
        .text( input.val().replace( /\S*$/, "" ) )
        .css( style )
        .insertAfter( input );
    offset = Math.min(
        Math.max( offset + div.width(), 0 ),
        input.width() - widget.width()
    );
    div.remove();

    pos.at = "left+" + offset + " bottom";
    input.autocomplete( "option", "position", pos );

    widget.position( $.extend(  of: input , pos ) );

更新:修正自动完成框定位

更新 2:另一个自动完成框定位修复

【讨论】:

这段代码运行良好,尤其是下拉菜单不会随着光标的移动而移动,而是出现在下一个单词开头的光标下方。但问题与@Dom 的答案相同,如果输入框的位置更改为新位置,下拉菜单将出现在旧位置。例如,在 jsfiddle 中,当您尝试将 css、html、js 和 result 之间的中间条拖到左侧时,意味着如果输入框位置 g 更改为标签的右侧而不是标签下方现在,下拉菜单单独出现在标签下方。 您的代码的另一个优点是您消除了对 html &lt;span&lt;div 的要求。我猜想下拉菜单没有出现在输入框的实际位置下方是因为在自动完成功能下使用open: 实现了代码。在答案中,@Vadim 的代码非常好,因为他使用.on 将其作为外部函数实现,但缺点是他的代码需要&lt;span,这与您的代码不同,而且在他的代码中,下拉菜单与光标移动。如果可以解决上述评论中的位置问题,那么您的代码就是答案。 太棒了!但是假设输入框有“This is a long sentence”之类的词,假设字母“s”有一些更大的建议,那么发生的情况是,作为最后一个词,它的建议超出了输入框的宽度,是否可以让它始终包含在框宽度内?含义假设建议使用“战略”一词,建议大小的增加不应移动到输入框的右侧而是左侧,如果建议与输入框一样大或大于输入框,则下拉列表应该只有输入框宽度为最大值。 @Javier***lyn 您可以绑定偏移量计算,使其不超过(width of input) - (width of popup)。这并不难。 我将代码的“offset =”更改为 offset = Math.min( offset + div.width(), input.width() - 0.62*input.width());。它之所以有效,是因为它显示了较小的术语宽度并保持在输入的大小范围内。在您的代码中,您将下拉列表的大小保持为取决于字长,如何创建一个值来获得像 div.width() 或 input.width() 这样的“与字相关的下拉菜单”的宽度?所以我可以将input.width() - 0.62*input.width()); 替换为input.width() - (width of that dropdown box that is different for each word)【参考方案2】:

使用JQuery's multiple demo,您可以创建一个隐藏的 span 元素

<span id="characterSpan" style="visibility: hidden;"></span>

并使用它来存储输入的val()

从那里,使用.width() 获取跨度的宽度并使用自动完成的open( event, ui ) 事件:

open: function( event, ui ) 
        var span = $('#characterSpan');
        var width = span.width();
        width > $('#query').width() ? 
        width = parseInt($('#query').position().left + $('#query').width()) : 
        width = parseInt($('#query').position().left + width);

        $('.ui-autocomplete.ui-menu').css('left', width + 'px');
    

演示:http://jsfiddle.net/dirtyd77/5ySF9/8/

希望我解释得足够清楚,如果您有任何问题,请告诉我!

【讨论】:

这个代码也可以工作,但是我发现这个代码有一个错误或问题,当你去 jsfiddle 时,当前输入框位于标签“标签程序....”下面,所以它显示正确但是,当“结果”的窗口窗格或显示结果的窗口通过向左拖动中间栏来调整大小时,使得输入框出现在标签的右侧,下拉菜单将出现在其第一个位置,意味着代码认为下拉框仍然在标签下方,而不是新位置,即标签右侧。 select: function( event, ui ) 中,您是否添加了:$('#characterSpan').html($(this).val());?确保复制/粘贴我的 jsfiddle 中使用的内容。那有帮助吗?如果不是,请提供您当前代码的示例。 是的,我补充说,我在您提供的同一个小提琴上测试了我的上述评论,但问题是当输入框位于不同的位置时,下拉菜单出现在固定位置。所以为了测试一下,在jsfiddle中有4个面板,html,css,js,result,还有一个中间栏,我把中间栏拖到左边,结果输入框的位置发生了变化,下拉菜单是在我拖动栏之前出现在它的位置。但是你的代码可以做我想做的事情。 哦!我明白你现在在说什么了!然后只需使用.position().left 获取输入的位置。我已经对我的初始帖子进行了更改。这应该可以解决问题,但如果您有任何其他问题,请告诉我! 好吧,编辑似乎也不起作用,因为我遇到的问题是,当我输入单词并退格时,下拉菜单仍然保留在我输入最后一个单词的位置,并且位置也发生了变化关于输入框位置的下拉列表没有得到更正。【参考方案3】:

我的答案基于this question 的答案。正如那个答案中提到的,IE

基本上,您创建一个跨度,您可以在其中写出输入元素的内容。然后,您使用此跨度的宽度来计算偏移量。 JqueryUI 提供了一个position 钩子,您可以在其中实际偏移该值。 所以HTML

<input id="query" style="width: 300px" />
<span id="faux" style="display:none" />

还有 JS

    var query= $("#query");
    var faux = $("#faux");
    query.autocomplete(
        source: mySource
    ).on("keydown", function() 
        var off = query.selectionStart;
        faux.text(query.val().substring(0, off).replace(/\s/g, "\u00a0"));
        query.autocomplete("option", "position", 
              my: "left top", at: "left+" + faux.outerWidth() + " bottom");
    );

Working JSFiddle

【讨论】:

您的代码在 jsfiddle 工作,但在我调用自动完成的函数中不起作用。等一下,我会用代码更新我的问题。 你使用的是什么版本的 jQuery @Javier***lyn?您可以尝试将on 调用替换为bind。或者只是将它放在您现有的 keydown 方法中。 我使用的是 1.90 和 1.10.0.0(我不知道这些是不是版本,但是 js 文件是 jquery-1.9.0.js 和 jquery-ui-1.10.0.custom .js) 您如何尝试集成我发布的代码?你添加了人造跨度吗?您在哪里添加了新的 keydown 事件处理程序? 我将 .on 函数放在了错误的位置,这就是它不起作用的原因。我在最后一个 ');' 之后正确放置了它就在'return false'删除';'之后现在可以了。但是代码还有一些问题需要修复,下拉框会随着光标的移动而移动,第一个单词后下拉框的宽度必须取决于相关下拉列表中最大单词的宽度,并且宽度或下拉框应包含在输入框的宽度内。简而言之,类似于 Google 搜索框的代码是合适的。

以上是关于每个单词下方的jQuery-ui自动完成下拉菜单的主要内容,如果未能解决你的问题,请参考以下文章

jquery-ui 自动完成:在下拉列表中显示对 ID-Name 的列表,但 ID 隐藏

使用jQuery-UI来实现一个Ajax的自动完成功能(自动填充搜索框的下拉值)

如何使用 KnockoutJS 和 JQuery UI 创建自动完成组合框

jquery ui 选择菜单自动填充

使用带有多个输入字段的 jquery-ui 自动完成

单独更改 jquery-ui 自动完成小部件的宽度