Comment puis-je personnaliser le formatage des résultats du plug-in de saisie semi-automatique?


Réponses:


233

Saisie semi-automatique avec suggestion en direct

Oui, vous pouvez si vous effectuez une saisie semi-automatique de monkey-patch.

Dans le widget de saisie semi-automatique inclus dans la v1.8rc3 de jQuery UI, la fenêtre contextuelle de suggestions est créée dans la fonction _renderMenu du widget de saisie semi-automatique. Cette fonction est définie comme ceci:

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

La fonction _renderItem est définie comme ceci:

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

Donc, ce que vous devez faire est de remplacer ce _renderItem fn par votre propre création qui produit l'effet souhaité. Cette technique, redéfinissant une fonction interne dans une bibliothèque, que j'apprends, s'appelle le monkey-patching . Voici comment je l'ai fait:

  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 );
      };
  }

Appelez cette fonction une fois $(document).ready(...).

Maintenant, c'est un hack, car:

  • il y a un obj regexp créé pour chaque élément rendu dans la liste. Cet obj d'expression régulière doit être réutilisé pour tous les éléments.

  • il n'y a pas de classe css utilisée pour le formatage de la pièce terminée. C'est un style en ligne.
    Cela signifie que si vous aviez plusieurs saisie semi-automatique sur la même page, ils recevraient tous le même traitement. Un style css résoudrait cela.

... mais cela illustre la technique principale, et cela fonctionne pour vos besoins de base.

texte alternatif

exemple de travail mis à jour: http://output.jsbin.com/qixaxinuhe


Pour conserver la casse des chaînes de correspondance, par opposition à l'utilisation de la casse des caractères tapés, utilisez cette ligne:

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

En d'autres termes, à partir du code d'origine ci-dessus, il vous suffit de remplacer this.termpar "$&".


MODIFIER
Ce qui précède modifie chaque widget de saisie semi-automatique de la page. Si vous ne souhaitez en modifier qu'une seule, consultez cette question:
Comment corriger * une seule * instance de saisie semi-automatique sur une page?


Merci Cheeso. Avez-vous un lien jsbin pour cela?
dev.e.loper

1
Notez que si vous enchaînez les choses, il est important de réinitialiser le contexte: oldFn.apply (this, [ul, item]);
emanaton

Merci beaucoup! Ce serait génial si cela devenait une partie de l'interface utilisateur jQuery.
David Ryder

4
Une chose que je dirais est que si vous voulez qu'il met en gras le résultat dans n'importe quelle partie de la chaîne correspondante (pas seulement le début) modifiez la ligne RegExp en ceci: var re = new RegExp (this.term);
David Ryder

1
La saisie semi-automatique de JQueryUI semble effectuer une recherche insensible à la casse par défaut, il est donc logique d'ajouter l'indicateur "i" dans l'objet RegExp. Il n'y a pas non plus de raison d'utiliser le "^" dans l'expression régulière comme @DavidRyder l'a mentionné. Comme: var re = new RegExp(this.term, "i"); Super article!
Sam

65

cela fonctionne également:

       $.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);
        };

une combinaison des réponses de @ Jörn Zaefferer et @ Cheeso.


J'ai mieux aimé celui-ci, car il correspond au mot entier.
atp

3
Cela fonctionne bien. La seule chose à surveiller est que item.label est remplacé. Pour moi, j'obtenais "<strong ..." dans ma zone de texte. Le simple fait d'assigner le résultat de remplacement à une autre variable a effacé cela. Vous n'auriez pas ce problème si vos données contiennent une propriété de valeur qui est placée dans la zone de texte.
Kijana Woodard

cela ne fonctionne pas si vous avez une saisie semi-automatique qui prend en charge plusieurs valeurs. Aucune suggestion?
leora

Le moyen le plus simple de mettre en évidence le texte dans vos résultats de saisie semi-automatique. Évidemment, recherchez les bugs comme indiqué par leora et Kijana ci
adamst85

@RNKushwaha n'importe où avant d'appeler$().autocomplete()
Brian Leishman

8

Super utile. Je vous remercie. +1.

Voici une version allégée qui trie sur "La chaîne doit commencer par le terme":

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
Merci Orolo, j'utilisais la saisie semi-automatique à plusieurs endroits et je voulais un endroit central où je peux faire le changement pour n'afficher que les résultats commençant par les caractères tapés et celui-ci est exactement ce dont j'avais besoin!
dreamerkumar

Merci. C'est la meilleure solution de toutes. Cela fonctionne pour insensible à la casse.
Aniket Kulkarni

7

Voilà, un exemple complet fonctionnel:

<!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>

J'espère que cela t'aides


6

jQueryUI 1.9.0 modifie le fonctionnement de _renderItem.

Le code ci-dessous prend en compte ce changement et montre également comment je faisais la mise en correspondance des tons clairs en utilisant le plugin jQuery Autocomplete de Jörn Zaefferer. Il mettra en évidence tous les termes individuels dans le terme de recherche global.

Depuis que je suis passé à l'utilisation de Knockout et de jqAuto, j'ai trouvé que c'était un moyen beaucoup plus simple de styliser les résultats.

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();
});

Quand je recherche avec des caractères comme '(' cela provoque une erreur ("Uncaught SyntaxError: Invalid regular expression: / (sam | at | () /: Unterminated group") quand même pour résoudre ce problème en empêchant la collission avec regex?
Idan Shechter

Réponse géniale! J'adore qu'il met en évidence les termes, peu importe où ils apparaissent. Très cool. Merci d'avoir mis à jour le message. Une question que j'ai eue concerne la classe .ui-menu-item-highlight que vous utilisez. Est-ce que cela devrait être défini par jquery-ui ou par les consommateurs? J'ai changé le nom de la classe pour qu'il corresponde à mes propres moyens et j'ai simplement mis le poids de la police en gras. .jqAutocompleteMatch { font-weight: bold; }
Sam

@IdanShechter Excellent commentaire. Une certaine logique pour échapper à l' this.termexpression rationnelle for doit être utilisée avant d'effectuer tout traitement. Voir Chaîne d'échappement à utiliser dans Javascript regex comme l'une des nombreuses réponses à la façon de procéder.
Sam

3

pour un moyen encore plus simple, essayez ceci:

$('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

Voici une répétition de la solution de Ted de Koning. Il comprend :

  • Recherche insensible à la casse
  • Recherche de nombreuses occurrences de la chaîne recherchée
$.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
Je pense que vous réinventez la roue! Pourquoi n'utilisez-vous pas des expressions régulières plus rapides, plus simples et plus compactes que tout ce code?
George Mavritsakis

2

Voici une version qui ne nécessite aucune expression régulière et qui correspond à plusieurs résultats dans l'étiquette.

$.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);
};

C'est probablement la meilleure solution, mais string.split n'est capable que de correspondances sensibles à la casse, je crois.
Noel Abrahams

Avez-vous un moyen de faire correspondre des mots en fonction de la non-sensibilité à la casse? Parce que si j'ai une recherche "alfa", elle ne mettra pas en évidence "Alfa"
Patrick


1

Voici ma version:

  • Utilise les fonctions DOM au lieu de RegEx pour casser des chaînes / injecter des balises span
  • Seule la saisie semi-automatique spécifiée est affectée, pas tous
  • Fonctionne avec la version 1.9.x de l'interface utilisateur
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);
};

Mettre en évidence un exemple de texte correspondant


1

vous pouvez utiliser le code suivant:

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;
    }
});

et logique:

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

il crée un widget personnalisé qui peut remplacer _renderItemsans écraser le _renderItemprototype de plugin original.

dans mon exemple, j'ai également utilisé la fonction de rendu d'origine pour simplifier le code

c'est une chose importante si vous souhaitez utiliser le plugin à différents endroits avec une vue différente de la saisie semi-automatique et que vous ne voulez pas casser votre code.


En général, il est préférable de passer du temps à répondre à des questions plus récentes ou sans réponses plutôt que de répondre à une question de 6 ans à laquelle on a déjà répondu.
zchrykng

0

Si vous utilisez à la place le plugin tiers, il a une option de surbrillance: http://docs.jquery.com/Plugins/Autocomplete/autocomplete#url_or_dataoptions

(voir l'onglet Options)


oui, je suis au courant de ce plug-in. Cependant, nous utilisons jQueryUI dans notre application, donc ce serait bien de le faire fonctionner avec le plug-in
jQueryUI Autocomplete

1
Depuis le 23/06/2010, le plugin jQuery Autocomplete est devenu obsolète au profit du plugin jQuery UI Autocomplete. Voir bassistance.de/jquery-plugins/jquery-plugin-autocomplete pour plus d'informations
shek

0

Pour prendre en charge plusieurs valeurs, ajoutez simplement la fonction suivante:

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>");
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.