it-swarm.com.de

Autovervollständigung im Twitter-Stil im Textbereich

Ich suche eine Javascript-Autocomplete-Implementierung, die Folgendes beinhaltet:

  • Kann in einem HTML-Textbereich verwendet werden
  • Ermöglicht die Eingabe von normalem Text ohne Aufruf der Autovervollständigung
  • Erkennt das Zeichen @ und startet die automatische Vervollständigung, wenn es eingegeben wird
  • Lädt eine Liste von Optionen über AJAX

Ich glaube, das ähnelt dem, was Twitter beim Taggen in einem Tweet tut, aber ich kann keine schöne, wiederverwendbare Implementierung finden.
Eine Lösung mit jQuery wäre perfekt.

Vielen Dank.

46
Martin Wiboe

Ich konnte keine Lösung finden, die meinen Anforderungen perfekt entsprach. Daher bekam ich Folgendes:

Ich verwende das Ereignis jQuery keypress(), um zu überprüfen, ob der Benutzer das Zeichen @ drückt.
Wenn dies der Fall ist, wird ein modaler Dialog mit der jQuery-Benutzeroberfläche angezeigt. Dieses Dialogfeld enthält ein Textfeld für die automatische Vervollständigung (viele Optionen können hier verwendet werden, ich empfehle jedoch jQuery Tokeninput ).
Wenn der Benutzer eine Option im Dialogfeld auswählt, wird ein Textfeld in das Textfeld eingefügt und der Dialog wird geschlossen.

Dies ist nicht die eleganteste Lösung, aber sie funktioniert und erfordert im Vergleich zu meinem ursprünglichen Design keine zusätzlichen Tastendrücke.

Bearbeiten
Im Grunde haben wir unser großes Textfeld, in das der Benutzer Text eingeben kann. Er sollte in der Lage sein, einen Benutzer "zu kennzeichnen" (dies bedeutet nur das Einfügen von #<userid> in den Text). Ich gehe an das jQuery-Schlüsselereignis an und erkenne das Zeichen @ mit (e.which == 64), um ein Modal mit einem Textfeld für die Auswahl der zu tagenden Benutzer anzuzeigen.

Das Fleisch der Lösung ist einfach dieser modale Dialog mit einem Textfeld jQuery Tokeninput . Während der Benutzer hier eingibt, wird die Benutzerliste über AJAX geladen. In den Beispielen auf der Website finden Sie Informationen zur richtigen Verwendung. Wenn der Benutzer das Dialogfeld schließt, füge ich die ausgewählten IDs in das große Textfeld ein.

4
Martin Wiboe

Ich bin mir sicher, dass Ihr Problem längst gelöst ist, aber jquery-textcomplete sieht so aus, als würde es die Aufgabe erledigen.

18
Kevin Schaper

Eine weitere großartige Bibliothek, die dieses Problem löst, ist At.js

Quelle

Demo

17
Jernej Novak

Hast du das probiert?

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

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

Es ist ziemlich einfach zu implementieren.

7
Andrej Kaurin

Versuche dies:

(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/ Dieser Link wird Ihnen helfen

6
Duy NGuyen

Ich habe ein Meteor - Paket für diesen Zweck erstellt. Das Datenmodell von Meteor ermöglicht eine schnelle Suche nach mehreren Regeln mit benutzerdefinierten gerenderten Listen. Wenn Sie Meteor nicht für Ihre Web-App verwenden, werden Sie (so glaube ich) leider nichts Besonderes für die automatische Vervollständigung finden.

Autocompleting-Benutzer mit @, wobei Online-Benutzer grün angezeigt werden:

enter image description here

In derselben Zeile automatisch etwas anderes mit Metadaten und Bootstrap-Symbolen vervollständigen:

enter image description here

Gabeln, ziehen und verbessern:

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

4
Andrew Mao

Neulich musste ich mich diesem Problem stellen und so habe ich es festgenagelt ...

  1. Rufen Sie den String-Index an der Cursorposition im Textbereich ab, indem Sie selectionStart verwenden 
  2. schneiden Sie die Zeichenfolge von Index 0 zur Cursorposition
  3. Einfügen in einen Bereich (da der Bereich mehrere Rahmen hat)
  4. Rufen Sie die Abmessungen des Rahmens mithilfe von element.getClientRects () relativ zum Ansichtsport ab. (hier ist die MDN-Referenz )
  5. Berechnen Sie den oberen und linken Rand und führen Sie das Dropdown-Menü aus

Dies funktioniert in allen aktuellen Browsern. habe nicht an alten getestet

Hier ist Arbeitskorb

3
selvagsz

Ein weiteres Plugin, das ähnliche Funktionen bietet:

AutoSuggest

Sie können es mit benutzerdefinierten Auslösern oder ohne Auslöser verwenden. Funktioniert mit Eingabefeldern, Textbereichen und Inhaltseditables. Und jQuery ist keine Abhängigkeit.

1
AvcS

Diese kleine Erweiterung scheint dem, was gefragt wurde, am nächsten zu sein. Da es klein ist, kann es leicht verstanden und modifiziert werden. http://lookapanda.github.io/jquery-hashtags/

0
valk