Javascript textarea 中的 Twitter 风格自动完成

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6157992/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 20:29:28  来源:igfitidea点击:

Twitter-style autocomplete in textarea

javascriptjqueryhtmlajaxtwitter

提问by Martin Wiboe

I am looking for a Javascript autocomplete implementation which includes the following:

我正在寻找一个 Javascript 自动完成实现,其中包括以下内容:

  • Can be used in a HTML textarea
  • Allows for typing regular text without invoking autocomplete
  • Detects the @character and starts autocomplete when it is typed
  • Loads list of options through AJAX
  • 可以在 HTML textarea 中使用
  • 允许在不调用自动完成的情况下输入常规文本
  • 检测@字符并在键入时启动自动完成
  • 通过 AJAX 加载选项列表

I believe that this is similar to what Twitter is doing when tagging in a tweet, but I can't find a nice, reusable implementation.
A solution with jQuery would be perfect.

我相信这与 Twitter 在推文中标记时所做的类似,但我找不到一个很好的、可重用的实现。
使用 jQuery 的解决方案将是完美的。

Thanks.

谢谢。

采纳答案by Martin Wiboe

I could not find any solution that matched my requirements perfectly, so I ended up with the following:

我找不到任何完全符合我要求的解决方案,所以我最终得到了以下结果:

I use the jQuery keypress()event to check for the user pressing the @character.
If this is the case, a modal dialog is shown using jQuery UI. This dialog contains an autocomplete text field (many options can be used here, but I recommmend jQuery Tokeninput)
When the user selects an option in the dialog, a tag is added to the text field and the dialog is closed.

我使用 jQuerykeypress()事件来检查用户是否按下了@字符。
如果是这种情况,将使用 jQuery UI 显示一个模式对话框。这个对话框包含一个自动完成文本字段(这里可以使用很多选项,但我推荐jQuery Tokeninput
当用户在对话框中选择一个选项时,一个标签被添加到文本字段并关闭对话框。

This is not the most elegant solution, but it works and it does not require extra keypresses compared to my original design.

这不是最优雅的解决方案,但它有效并且与我的原始设计相比不需要额外的按键。

Edit
So basically, we have our large text box where the user can enter text. He should be able to "tag" a user (this just means inserting #<userid>in the text). I attach to the jQuery keyup event and detect the @character using (e.which == 64)to show a modal with a text field for selecting the users to tag.

编辑
所以基本上,我们有我们的大文本框,用户可以在其中输入文本。他应该能够“标记”用户(这只是意味着插入#<userid>文本)。我附加到 jQuery keyup 事件并检测@字符,(e.which == 64)用于显示带有文本字段的模式,用于选择要标记的用户。

The meat of the solution is simply this modal dialog with a jQuery Tokeninputtext box. As the user types here, the list of users is loaded through AJAX. See the examples on the website for how to use it properly. When the user closes the dialog, I insert the selected IDs into the large text box.

解决方案的核心就是这个带有jQuery Tokeninput文本框的模态对话框。当用户在此处键入时,用户列表将通过 AJAX 加载。有关如何正确使用它,请参阅网站上的示例。当用户关闭对话框时,我将选定的 ID 插入到大文本框中。

回答by Kevin Schaper

I'm sure your problem is long since solved, but jquery-textcompletelooks like it would do the job.

我相信你的问题早就解决了,但jquery-textcomplete看起来可以完成这项工作。

回答by Jernej Novak

Another great library which solves this problem At.js

另一个解决这个问题的很棒的库 At.js

Source

来源

Demo

演示

回答by Andrej Kaurin

Have you tried this

你有没有试过这个

GITHUB: https://github.com/podio/jquery-mentions-input

GITHUB:https: //github.com/podio/jquery-mentions-input

DEMO/CONFIG: http://podio.github.io/jquery-mentions-input/

演示/配置:http: //podio.github.io/jquery-mentions-input/

It is pretty simple to implement.

实现起来非常简单。

回答by Duy NGuyen

Try this:

尝试这个:

(function($){
    
        $.widget("ui.tagging", {
            // default options
            options: {
                source: [],
                maxItemDisplay: 3,
                autosize: true,
                animateResize: false,
                animateDuration: 50
            },
            _create: function() {
                var self = this;
                
                this.activeSearch = false;
                this.searchTerm = "";
                this.beginFrom = 0;
    
                this.wrapper = $("<div>")
                    .addClass("ui-tagging-wrap");
                
                this.highlight = $("<div></div>");
                
                this.highlightWrapper = $("<span></span>")
                    .addClass("ui-corner-all");
    
                this.highlightContainer = $("<div>")
                    .addClass("ui-tagging-highlight")
                    .append(this.highlight);
    
                this.meta = $("<input>")
                    .attr("type", "hidden")
                    .addClass("ui-tagging-meta");
    
                this.container = $("<div></div>")
                    .width(this.element.width())
                    .insertBefore(this.element)
                    .addClass("ui-tagging")
                    .append(
                        this.highlightContainer, 
                        this.element.wrap(this.wrapper).parent(), 
                        this.meta
                    );
                
                var initialHeight = this.element.height();
                
                this.element.height(this.element.css('lineHeight'));
                
                this.element.keypress(function(e) {
                    // activate on @
                    if (e.which == 64 && !self.activeSearch) {
                        self.activeSearch = true;
                        self.beginFrom = e.target.selectionStart + 1;
                    }
                    // deactivate on space
                    if (e.which == 32 && self.activeSearch) {
                        self.activeSearch = false;
                    }
                }).bind("expand keyup keydown change", function(e) {
                    var cur = self.highlight.find("span"),
                        val = self.element.val(),
                        prevHeight = self.element.height(),
                        rowHeight = self.element.css('lineHeight'),
                        newHeight = 0;
                    cur.each(function(i) {
                        var s = $(this);
                        val = val.replace(s.text(), $("<div>").append(s).html());
                    });
                    self.highlight.html(val);
                    newHeight = self.element.height(rowHeight)[0].scrollHeight;
                    self.element.height(prevHeight);
                    if (newHeight < initialHeight) {
                        newHeight = initialHeight;
                    }
                    if (!$.browser.mozilla) {
                        if (self.element.css('paddingBottom') || self.element.css('paddingTop')) {
                            var padInt =
                                parseInt(self.element.css('paddingBottom').replace('px', '')) + 
                                parseInt(self.element.css('paddingTop').replace('px', ''));
                            newHeight -= padInt;
                        }
                    }
                    self.options.animateResize ?
                        self.element.stop(true, true).animate({
                                height: newHeight
                            }, self.options.animateDuration) : 
                        self.element.height(newHeight);
                    
                    var widget = self.element.autocomplete("widget");
                        widget.position({
                            my: "left top",
                            at: "left bottom",
                            of: self.container
                        }).width(self.container.width()-4);
                    
                }).autocomplete({
                    minLength: 0,
                    delay: 0,
                    maxDisplay: this.options.maxItemDisplay,
                    open: function(event, ui) {
                        var widget = $(this).autocomplete("widget");
                        widget.position({
                            my: "left top",
                            at: "left bottom",
                            of: self.container
                        }).width(self.container.width()-4);
                    },
                    source: function(request, response) {
                        if (self.activeSearch) {
                            self.searchTerm = request.term.substring(self.beginFrom); 
                            if (request.term.substring(self.beginFrom - 1, self.beginFrom) != "@") {
                                self.activeSearch = false;
                                self.beginFrom = 0;
                                self.searchTerm = "";
                            }
                            if (self.searchTerm != "") {
                                
                                if ($.type(self.options.source) == "function") {
                                    self.options.source(request, response);                   
                                } else {
                                    var re = new RegExp("^" + escape(self.searchTerm) + ".+", "i");
                                    var matches = [];
                                    $.each(self.options.source, function() {
                                        if (this.label.match(re)) {
                                            matches.push(this);
                                        }
                                    });
                                    response(matches);
                                }
                            }
                        }
                    },
                    focus: function() {
                        // prevent value inserted on focus
                        return false;
                    },
                    select: function(event, ui) {
                        self.activeSearch = false;
                        //console.log("@"+searchTerm, ui.item.label);
                        this.value = this.value.replace("@" + self.searchTerm, ui.item.label) + ' ';
                        self.highlight.html(
                            self.highlight.html()
                                .replace("@" + self.searchTerm,
                                         $("<div>").append(
                                             self.highlightWrapper
                                                 .text(ui.item.label)
                                                 .clone()
                                         ).html()+' ')
                        );
                            
                        self.meta.val((self.meta.val() + " @[" + ui.item.value + ":]").trim());
                        return false;
                    }
                });
    
            }
        });
body, html {
        font-family: "lucida grande",tahoma,verdana,arial,sans-serif;
    }
    
    .ui-tagging {
        position: relative;
        border: 1px solid #B4BBCD;
        height: auto;
    }
    
    .ui-tagging .ui-tagging-highlight {
        position: absolute;
        padding: 5px;
        overflow: hidden;
    }
    .ui-tagging .ui-tagging-highlight div {
        color: transparent;
        font-size: 13px;
        line-height: 18px;
        white-space: pre-wrap;
    }
    
    .ui-tagging .ui-tagging-wrap {
        position: relative;
        padding: 5px;
        overflow: hidden;
        zoom: 1;
        border: 0;
    }
    
    .ui-tagging div > span {
        background-color: #D8DFEA;
        font-weight: normal !important;
    }
    
    .ui-tagging textarea {
        display: block;
        font-family: "lucida grande",tahoma,verdana,arial,sans-serif;
        background: transparent;
        border-width: 0;
        font-size: 13px;
        height: 18px;
        outline: none;
        resize: none;
        vertical-align: top;
        width: 100%;
        line-height: 18px;
        overflow: hidden;
    }
    
    .ui-autocomplete {
        font-size: 13px;
        background-color: white;
        border: 1px solid black;
        margin-bottom: -5px;
        width: 0;
    }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea></textarea>

http://jsfiddle.net/mekwall/mcWnL/52/This link will help you

http://jsfiddle.net/mekwall/mcWnL/52/此链接将帮助您

回答by Andrew Mao

I've created a Meteorpackage for this purpose. Meteor's data model allows for fast multi-rule searching with custom rendered lists. If you're not using Meteor for your web app, (I believe) you unfortunately won't find anything this awesome for autocompletion.

为此,我创建了一个Meteor包。Meteor 的数据模型允许使用自定义呈现列表进行快速多规则搜索。如果您没有将 Meteor 用于您的网络应用程序,(我相信)您很遗憾将找不到任何如此出色的自动完成功能。

Autocompleting users with @, where online users are shown in green:

使用 自动完成用户@,在线用户显示为绿色:

enter image description here

在此处输入图片说明

In the same line, autocompleting something else with metadata and bootstrap icons:

在同一行中,使用元数据和引导程序图标自动完成其他内容:

enter image description here

在此处输入图片说明

Fork, pull, and improve:

分叉、拉动和改进:

https://github.com/mizzao/meteor-autocomplete

https://github.com/mizzao/meteor-autocomplete

回答by selvagsz

Recently i had to face this problem and this is how i nailed down...

最近我不得不面对这个问题,这就是我确定的方式......

  1. Get the string index at the cursor position in the textarea by using selectionStart
  2. slice the string from index 0 to the cursor position
  3. Insert it into a span (since span has multiple border boxes)
  4. Get the dimensions of the border box using element.getClientRects() relative to the view port. (here is the MDN Reference)
  5. Calculate the top and left and feed it to the dropdown
  1. 使用 selectionStart 获取 textarea 中光标位置处的字符串索引
  2. 将字符串从索引 0 切片到光标位置
  3. 将它插入一个跨度(因为跨度有多个边框框)
  4. 使用 element.getClientRects() 获取边框框相对于视口的尺寸。(这里是MDN 参考
  5. 计算顶部和左侧并将其提供给下拉列表

This works in all latest browsers. haven't tested at old ones

这适用于所有最新的浏览器。没有在旧的测试

Here is Working bin

这是工作箱

回答by AvcS

Another plugin which provides similar functionality:

另一个提供类似功能的插件:

AutoSuggest

自动建议

You can use it with custom triggers or you can use it without any triggers. Works with input fields, textareas and contenteditables. And jQuery is not a dependency.

您可以将它与自定义触发器一起使用,也可以在没有任何触发器的情况下使用它。适用于输入字段、textareas 和 contenteditables。jQuery 不是依赖项。

回答by valk

This small extension seems to be the closest at least in presentation to what was asked. Since it's small, it can be easily understood and modified. http://lookapanda.github.io/jquery-hashtags/

这个小的扩展似乎是最接近的,至少在表现上最接近被问到的问题。因为它很小,所以很容易理解和修改。http://lookapanda.github.io/jquery-hashtags/

回答by Ashley Staggs

THISshould work. With regards to the @ kicking off the search, just add (dynamically or not) the symbol to the beginning of each possible search term.

应该有效。关于启动搜索的@,只需在每个可能的搜索词的开头添加(动态或不动态)符号。