Jak mogę niestandardowo sformatować wyniki wtyczki autouzupełniania?


Odpowiedzi:


233

Autouzupełnianie za pomocą sugestii na żywo

Tak, możesz, jeśli automatycznie uzupełniasz małpy.

W widgecie autouzupełniania zawartym w wersji 1.8rc3 interfejsu użytkownika jQuery wyskakujące okienko sugestii jest tworzone w funkcji _renderMenu widżetu autouzupełniania. Ta funkcja jest zdefiniowana w następujący sposób:

_renderMenu: function( ul, items ) {
    var self = this;
    $.each( items, function( index, item ) {
        self._renderItem( ul, item );
    });
},

Funkcja _renderItem jest zdefiniowana w następujący sposób:

_renderItem: function( ul, item) {
    return $( "<li></li>" )
        .data( "item.autocomplete", item )
        .append( "<a>" + item.label + "</a>" )
        .appendTo( ul );
},

Musisz więc zastąpić _renderItem fn własną kreacją, która daje pożądany efekt. Technika ta, redefiniująca wewnętrzną funkcję w bibliotece, której nauczyłem się, nazywa się małpą łataniem . Oto jak to zrobiłem:

  function monkeyPatchAutocomplete() {

      // don't really need this, but in case I did, I could store it and chain
      var oldFn = $.ui.autocomplete.prototype._renderItem;

      $.ui.autocomplete.prototype._renderItem = function( ul, item) {
          var re = new RegExp("^" + this.term) ;
          var t = item.label.replace(re,"<span style='font-weight:bold;color:Blue;'>" + 
                  this.term + 
                  "</span>");
          return $( "<li></li>" )
              .data( "item.autocomplete", item )
              .append( "<a>" + t + "</a>" )
              .appendTo( ul );
      };
  }

Wywołaj tę funkcję raz w $(document).ready(...).

To jest hack, ponieważ:

  • istnieje wyrażenie regularne obj. utworzone dla każdego elementu wyświetlanego na liście. To wyrażenie regularne obj powinno zostać ponownie użyte dla wszystkich pozycji.

  • nie ma klasy css używanej do formatowania ukończonej części. To styl inline.
    Oznacza to, że jeśli masz wiele autouzupełniania na tej samej stronie, wszystkie zostaną potraktowane tak samo. Styl CSS rozwiązałby ten problem.

... ale ilustruje główną technikę i działa zgodnie z Twoimi podstawowymi wymaganiami.

tekst alternatywny

zaktualizowany przykład roboczy: http://output.jsbin.com/qixaxinuhe


Aby zachować wielkość liter w pasujących łańcuchach, zamiast używać wielkości liter w wpisywanych znakach, użyj tej linii:

var t = item.label.replace(re,"<span style='font-weight:bold;color:Blue;'>" + 
          "$&" + 
          "</span>");

Innymi słowy, zaczynając od oryginalnego kodu powyżej, wystarczy zastąpić this.termgo "$&".


EDYTUJ
Powyższe zmienia wszystkie widżety autouzupełniania na stronie. Jeśli chcesz zmienić tylko jedną, zobacz to pytanie:
Jak załatać * tylko jedną * instancję autouzupełniania na stronie?


Dzięki Cheeso. Czy masz do tego link jsbin?
dev.e.loper

1
Zauważ, że jeśli wykonujesz operacje łańcuchowe, ważne jest, aby zresetować kontekst: oldFn.apply (this, [ul, item]);
emanaton

Dziękuję Ci bardzo! Byłoby wspaniale, gdyby stało się częścią interfejsu użytkownika jQuery.
David Ryder,

4
Powiedziałbym tylko, że jeśli chcesz, aby wynik był pogrubiony w dowolnej części dopasowanego ciągu (nie tylko na początku), zmodyfikuj wiersz RegExp do tego: var re = new RegExp (this.term);
David Ryder,

1
Wydaje się, że autouzupełnianie JQueryUI domyślnie przeprowadza wyszukiwanie bez rozróżniania wielkości liter, więc sensowne jest dodanie flagi „i” w obiekcie RegExp. Nie ma również powodu, aby używać „^” w wyrażeniu regularnym, jak wspomniał @DavidRyder. Podobnie jak: var re = new RegExp(this.term, "i"); Świetny post!
Sam

65

to też działa:

       $.ui.autocomplete.prototype._renderItem = function (ul, item) {
            item.label = item.label.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + $.ui.autocomplete.escapeRegex(this.term) + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
            return $("<li></li>")
                    .data("item.autocomplete", item)
                    .append("<a>" + item.label + "</a>")
                    .appendTo(ul);
        };

połączenie odpowiedzi @ Jörn Zaefferer i @ Cheeso.


Ten podobał mi się bardziej, ponieważ pasuje do całego słowa.
ATP

3
To działa dobrze. Jedyną rzeczą, na którą należy zwrócić uwagę, jest to, że item.label jest zastępowany. W moim przypadku w polu tekstowym pojawiło się „<strong ...”. Samo przypisanie wyniku zamiany do innej zmiennej wyjaśniło to. Nie miałbyś tego problemu, gdyby Twoje dane zawierały właściwość value, która zostanie umieszczona w polu tekstowym.
Kijana Woodard

to nie działa, jeśli masz autouzupełnianie, które obsługuje wiele wartości. jakieś sugestie?
leora

Najprostszy sposób na wyróżnienie tekstu w wynikach autouzupełniania. Oczywiście uważaj na błędy wskazane powyżej przez leorę i Kijanę
adamst85

@RNKushwaha gdziekolwiek, zanim zadzwonisz$().autocomplete()
Brian Leishman

8

Super pomocny. Dziękuję Ci. +1.

Oto uproszczona wersja z sortowaniem „Ciąg musi zaczynać się od terminu”:

function hackAutocomplete(){

    $.extend($.ui.autocomplete, {
        filter: function(array, term){
            var matcher = new RegExp("^" + term, "i");

            return $.grep(array, function(value){
                return matcher.test(value.label || value.value || value);
            });
        }
    });
}

hackAutocomplete();

1
Dzięki Orolo, korzystałem z autouzupełniania w wielu lokalizacjach i chciałem mieć centralne miejsce, w którym mogę wprowadzić zmiany, aby wyświetlać tylko wynik zaczynający się od wpisanych znaków, a ten jest dokładnie tym, czego potrzebowałem!
dreamerkumar

Dzięki. To najlepsze rozwiązanie ze wszystkich. Działa w przypadku braku rozróżniania wielkości liter.
Aniket Kulkarni

7

Oto pełny, funkcjonalny przykład:

<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Autocomplete - jQuery</title>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.css">
</head>
<body>
<form id="form1" name="form1" method="post" action="">
  <label for="search"></label>
  <input type="text" name="search" id="search" />
</form>

<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="http://code.jquery.com/ui/1.10.2/jquery-ui.js"></script>
<script>
$(function(){

$.ui.autocomplete.prototype._renderItem = function (ul, item) {
    item.label = item.label.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + $.ui.autocomplete.escapeRegex(this.term) + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
    return $("<li></li>")
            .data("item.autocomplete", item)
            .append("<a>" + item.label + "</a>")
            .appendTo(ul);
};


var availableTags = [
    "JavaScript",
    "ActionScript",
    "C++",
    "Delphi",
    "Cobol",
    "Java",
    "Ruby",
    "Python",
    "Perl",
    "Groove",
    "Lisp",
    "Pascal",
    "Assembly",
    "Cliper",
];

$('#search').autocomplete({
    source: availableTags,
    minLength: 3
});


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

Mam nadzieję że to pomoże


6

jQueryUI 1.9.0 zmienia sposób działania _renderItem.

Poniższy kod bierze pod uwagę tę zmianę, a także pokazuje, jak robiłem dopasowywanie podświetlenia za pomocą wtyczki jQuery Autocomplete Jörna Zaefferera. Podświetli wszystkie poszczególne terminy w ogólnym wyszukiwaniu.

Odkąd zacząłem używać Knockout i jqAuto, stwierdziłem, że jest to znacznie łatwiejszy sposób na stylizowanie wyników.

function monkeyPatchAutocomplete() {
   $.ui.autocomplete.prototype._renderItem = function (ul, item) {

      // Escape any regex syntax inside this.term
      var cleanTerm = this.term.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');

      // Build pipe separated string of terms to highlight
      var keywords = $.trim(cleanTerm).replace('  ', ' ').split(' ').join('|');

      // Get the new label text to use with matched terms wrapped
      // in a span tag with a class to do the highlighting
      var re = new RegExp("(" + keywords + ")", "gi");
      var output = item.label.replace(re,  
         '<span class="ui-menu-item-highlight">$1</span>');

      return $("<li>")
         .append($("<a>").html(output))
         .appendTo(ul);
   };
};

$(function () {
   monkeyPatchAutocomplete();
});

Kiedy wyszukuję za pomocą znaków takich jak '(' powoduje to błąd ("Uncaught SyntaxError: Invalid regular expression: / (sam | at | () /: Unterminated group") i tak rozwiązać ten problem zapobiegając kolizji z regex?
Idan Shechter

Świetna odpowiedź! Uwielbiam to, że podkreśla terminy niezależnie od tego, gdzie się pojawiają. Bardzo fajny. Dzięki za aktualizację posta. Jedno pytanie, które zadałem, dotyczyło używanej przez Ciebie klasy .ui-menu-item-highlight. Czy oczekuje się, że zostanie to zdefiniowane przez jQuery-ui czy przez konsumentów? Zmieniłem nazwę klasy, aby dostosować ją do własnych potrzeb i po prostu pogrubiłem grubość czcionki. .jqAutocompleteMatch { font-weight: bold; }
Sam

@IdanShechter Wielki komentarz. this.termPrzed wykonaniem jakiegokolwiek przetwarzania należy użyć logiki ucieczki przed wyrażeniem regularnym for. Zobacz Escape string for use in JavaScript regex jako jedną z wielu odpowiedzi, jak to zrobić.
Sam

3

aby uzyskać jeszcze łatwiejszy sposób, spróbuj tego:

$('ul: li: a[class=ui-corner-all]').each (function (){      
 //grab each text value 
 var text1 = $(this).text();     
 //grab user input from the search box
 var val = $('#s').val()
     //convert 
 re = new RegExp(val, "ig") 
 //match with the converted value
 matchNew = text1.match(re);
 //Find the reg expression, replace it with blue coloring/
 text = text1.replace(matchNew, ("<span style='font-weight:bold;color:green;'>")  + matchNew +    ("</span>"));

    $(this).html(text)
});
  }

3

Oto powtórzenie rozwiązania Teda de Koninga. Obejmuje:

  • Wyszukiwanie bez rozróżniania wielkości liter
  • Znajdowanie wielu wystąpień szukanego ciągu
$.ui.autocomplete.prototype._renderItem = function (ul, item) {

    var sNeedle     = item.label;
    var iTermLength = this.term.length; 
    var tStrPos     = new Array();      //Positions of this.term in string
    var iPointer    = 0;
    var sOutput     = '';

    //Change style here
    var sPrefix     = '<strong style="color:#3399FF">';
    var sSuffix     = '</strong>';

    //Find all occurences positions
    tTemp = item.label.toLowerCase().split(this.term.toLowerCase());
    var CharCount = 0;
    tTemp[-1] = '';
    for(i=0;i<tTemp.length;i++){
        CharCount += tTemp[i-1].length;
        tStrPos[i] = CharCount + (i * iTermLength) + tTemp[i].length
    }

    //Apply style
    i=0;
    if(tStrPos.length > 0){
        while(iPointer < sNeedle.length){
            if(i<=tStrPos.length){
                //Needle
                if(iPointer == tStrPos[i]){
                    sOutput += sPrefix + sNeedle.substring(iPointer, iPointer + iTermLength) + sSuffix;
                    iPointer += iTermLength;
                    i++;
                }
                else{
                    sOutput += sNeedle.substring(iPointer, tStrPos[i]);
                    iPointer = tStrPos[i];
                }
            }
        }
    }


    return $("<li></li>")
        .data("item.autocomplete", item)
        .append("<a>" + sOutput + "</a>")
        .appendTo(ul);
};

1
Myślę, że odkrywasz koło na nowo! Dlaczego nie używasz wyrażeń regularnych, które są szybsze, łatwiejsze i bardziej kompaktowe niż cały ten kod?
George Mavritsakis

2

Oto wersja, która nie wymaga żadnych wyrażeń regularnych i pasuje do wielu wyników na etykiecie.

$.ui.autocomplete.prototype._renderItem = function (ul, item) {
            var highlighted = item.label.split(this.term).join('<strong>' + this.term +  '</strong>');
            return $("<li></li>")
                .data("item.autocomplete", item)
                .append("<a>" + highlighted + "</a>")
                .appendTo(ul);
};

Jest to prawdopodobnie najlepsze rozwiązanie, ale uważam, że string.split może uwzględniać tylko dopasowania uwzględniające wielkość liter.
Noel Abrahams,

Czy jest jakiś sposób na dopasowanie słów bez rozróżniania wielkości liter? Bo jak mam wyszukanie "alfa" to nie podświetli "Alfa"
Patrick


1

Oto moja wersja:

  • Używa funkcji DOM zamiast RegEx do łamania ciągów / tagów wstrzyknięcia zakresu
  • Dotyczy to tylko określonego autouzupełniania, a nie wszystkich
  • Działa z interfejsem użytkownika w wersji 1.9.x
function highlightText(text, $node) {
    var searchText = $.trim(text).toLowerCase(),
        currentNode = $node.get(0).firstChild,
        matchIndex,
        newTextNode,
        newSpanNode;
    while ((matchIndex = currentNode.data.toLowerCase().indexOf(searchText)) >= 0) {
        newTextNode = currentNode.splitText(matchIndex);
        currentNode = newTextNode.splitText(searchText.length);
        newSpanNode = document.createElement("span");
        newSpanNode.className = "highlight";
        currentNode.parentNode.insertBefore(newSpanNode, currentNode);
        newSpanNode.appendChild(newTextNode);
    }
}
$("#autocomplete").autocomplete({
    source: data
}).data("ui-autocomplete")._renderItem = function (ul, item) {
    var $a = $("<a></a>").text(item.label);
    highlightText(this.term, $a);
    return $("<li></li>").append($a).appendTo(ul);
};

Podświetl przykład dopasowanego tekstu


1

możesz użyć następującego kodu:

lib:

$.widget("custom.highlightedautocomplete", $.ui.autocomplete, {
    _renderItem: function (ul, item) {
        var $li = $.ui.autocomplete.prototype._renderItem.call(this,ul,item);
        //any manipulation with li
        return $li;
    }
});

i logika:

$('selector').highlightedautocomplete({...});

tworzy niestandardowy widget, który może zastąpić oryginalny prototyp wtyczki _renderItembez nadpisywania _renderItem.

w moim przykładzie wykorzystałem również oryginalną funkcję renderującą do uproszczenia kodu

jest to ważne, jeśli chcesz używać wtyczki w różnych miejscach z innym widokiem autouzupełniania i nie chcesz łamać kodu.


Generalnie lepiej jest poświęcić czas na odpowiadanie na nowsze pytania lub te bez odpowiedzi, niż odpowiadanie na pytanie sprzed 6 lat, na które już udzielono odpowiedzi.
zchrykng

0

Jeśli zamiast tego użyjesz wtyczki innej firmy, ma ona opcję podświetlenia: http://docs.jquery.com/Plugins/Autocomplete/autocomplete#url_or_dataoptions

(patrz zakładka Opcje)


tak, wiem o tej wtyczce. jednak używamy jQueryUI w naszej aplikacji, więc byłoby miło, gdybyśmy działali z wtyczką jQueryUI Autocomplete
dev.e.loper

1
Od 23.06.2010 r. Wtyczka jQuery Autocomplete została wycofana na rzecz wtyczki jQuery UI Autocomplete. Zobacz bassistance.de/jquery-plugins/jquery-plugin-autocomplete, aby uzyskać więcej informacji
shek

0

Aby obsługiwać wiele wartości, po prostu dodaj następującą funkcję:

function getLastTerm( term ) {
  return split( term ).pop();
}

var t = String(item.value).replace(new RegExp(getLastTerm(this.term), "gi"), "<span class='ui-state-highlight'>$&</span>");
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.