¿Cómo puedo formatear a medida los resultados del complemento Autocompletar?


Respuestas:


233

Autocompletar con sugerencia en vivo

Sí, puedes hacerlo si parches de mono se autocompletan.

En el widget de autocompletar incluido en v1.8rc3 de jQuery UI, la ventana emergente de sugerencias se crea en la función _renderMenu del widget de autocompletar. Esta función se define así:

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

La función _renderItem se define así:

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

Entonces, lo que debe hacer es reemplazar ese _renderItem fn con su propia creación que produzca el efecto deseado. Esta técnica, redefiniendo una función interna en una biblioteca, he llegado a aprender que se llama parche de mono . Así es como lo hice:

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

Llama a esa función una vez $(document).ready(...).

Ahora, esto es un truco, porque:

  • Hay un obj regexp creado para cada elemento presentado en la lista. Ese regexp obj debe reutilizarse para todos los artículos.

  • no se utiliza ninguna clase css para formatear la parte completada. Es un estilo en línea.
    Esto significa que si tuviera múltiples autocompletados en la misma página, todos recibirían el mismo tratamiento. Un estilo CSS resolvería eso.

... pero ilustra la técnica principal y funciona para sus requisitos básicos.

texto alternativo

ejemplo de trabajo actualizado: http://output.jsbin.com/qixaxinuhe


Para preservar el caso de las cadenas de coincidencia, en lugar de usar el caso de los caracteres escritos, use esta línea:

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

En otras palabras, a partir del código original anterior, solo necesita reemplazarlo this.termpor "$&".


EDITAR
Lo anterior cambia cada widget de autocompletar en la página. Si desea cambiar solo uno, consulte esta pregunta:
¿Cómo parchear * solo una * instancia de Autocompletar en una página?


Gracias cheeso ¿Tienes un enlace jsbin para esto?
dev.e.loper

1
Tenga en cuenta que si encadena las cosas, es importante restablecer el contexto: oldFn.apply (this, [ul, item]);
emanaton

¡Muchas gracias! Sería increíble si esto se convirtiera en parte de jQuery UI.
David Ryder

44
Una cosa que diría es que si desea que el resultado aparezca en negrita en cualquier parte de la cadena coincidente (no solo el comienzo), modifique la línea RegExp a esto: var re = new RegExp (this.term);
David Ryder

1
El autocompletado de JQueryUI parece realizar una búsqueda que no distingue entre mayúsculas y minúsculas de forma predeterminada, por lo que tiene sentido agregar el indicador "i" en el objeto RegExp. Tampoco hay ninguna razón para usar la "^" en la expresión regular como mencionó @DavidRyder. Me gusta: ¡ var re = new RegExp(this.term, "i"); Gran publicación!
Sam

65

esto también funciona:

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

una combinación de las respuestas de @ Jörn Zaefferer y @ Cheeso.


Este me gustó más, ya que coincide con toda la palabra
atp

3
Esto funciona bien Lo único a tener en cuenta es que se está reemplazando ese elemento. Para mí, estaba obteniendo "<fuerte ..." en mi cuadro de texto. Solo asignando el resultado de reemplazo a otra variable se borró eso. No tendría este problema si sus datos contienen una propiedad de valor que se coloca en el cuadro de texto.
Kijana Woodard

esto no funciona si tiene un autocompletado que admite múltiples valores. ¿alguna sugerencia?
leora

La forma más sencilla de obtener resaltado de texto en sus resultados de autocompletar. Obviamente, busque los errores como lo señalan arriba Leora y Kijana
adamst85

@RNKushwaha en cualquier lugar antes de llamar$().autocomplete()
Brian Leishman el

8

Súper servicial Gracias. +1.

Aquí hay una versión ligera que se ordena en "La cadena debe comenzar con el término":

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
Gracias Orolo, estaba usando autocompletar en múltiples ubicaciones y quería un lugar central donde pudiera hacer el cambio para mostrar solo el resultado que comienza con los caracteres escritos y ¡este es exactamente lo que necesitaba!
dreamerkumar

Gracias. Esta es la mejor solución de todas. Funciona para mayúsculas y minúsculas.
Aniket Kulkarni

7

Aquí va, un ejemplo completo funcional:

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

Espero que esto ayude


6

jQueryUI 1.9.0 cambia el funcionamiento de _renderItem.

El siguiente código tiene en cuenta este cambio y también muestra cómo estaba haciendo la coincidencia de resaltado con el complemento jQuery Autocompletar de Jörn Zaefferer. Destacará todos los términos individuales en el término de búsqueda general.

Desde que pasé a usar Knockout y jqAuto, encontré que esta es una forma mucho más fácil de diseñar los resultados.

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

Cuando busco caracteres como '(', se produce un error ("Error de sintaxis no capturado: expresión regular no válida: / (sam | at | () /: grupo no terminado") de todos modos para resolver esto evitando la colisión con expresiones regulares
Idan Shechter

Respuesta impresionante! Me encanta que resalte los términos independientemente de dónde aparezcan. Muy genial. Gracias por actualizar la publicación. Una pregunta que tuve es sobre la clase .ui-menu-item-highlight que usas. ¿Se espera que esto sea definido por jquery-ui o por los consumidores? Cambié el nombre de la clase para adaptarme a mis propios medios y simplemente puse en negrita el peso de la fuente. .jqAutocompleteMatch { font-weight: bold; }
Sam

@IdanShechter Gran comentario. Se this.termdebe usar alguna lógica para escapar de regex antes de realizar cualquier procesamiento. Vea Escape string para usar en JavaScript regex como una de las muchas respuestas sobre cómo hacer esto.
Sam

3

para una manera aún más fácil, intente esto:

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

Aquí hay una repetición de la solución de Ted de Koning. Incluye :

  • Búsqueda insensible a mayúsculas y minúsculas
  • Encontrar muchas ocurrencias de la cadena buscada
$.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
¡Creo que estás reinventando la rueda! ¿Por qué no usa expresiones regulares que son más rápidas, fáciles y compactas que todo este código?
George Mavritsakis

2

Aquí hay una versión que no requiere ninguna expresión regular y coincide con múltiples resultados en la etiqueta.

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

Esta es probablemente la mejor solución, pero creo que string.split solo es capaz de distinguir entre mayúsculas y minúsculas.
Noel Abrahams

¿Tiene alguna forma de unir palabras basadas en mayúsculas y minúsculas? Porque si tengo una búsqueda "alfa", no resaltará "Alfa"
Patrick


1

Aquí está mi versión:

  • Utiliza funciones DOM en lugar de RegEx para romper cadenas / inyectar etiquetas de intervalo
  • Solo se ve afectado el autocompletado especificado, no todos
  • Funciona con UI versión 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);
};

Ejemplo de texto coincidente resaltado


1

puedes usar el siguiente código:

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

y lógica:

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

crea un widget personalizado que puede anular _renderItemsin sobrescribir el _renderItemprototipo original del complemento.

en mi ejemplo también utilicé la función de renderizado original para simplificar el código

Es importante si desea utilizar el complemento en diferentes lugares con diferentes vistas de autocompletar y no quiere romper su código.


En general, es mejor pasar el tiempo respondiendo preguntas más recientes o preguntas sin respuestas en lugar de responder una pregunta de 6 años que ya ha sido respondida.
zchrykng

0

Si en su lugar utiliza el complemento de terceros, tiene una opción destacada: http://docs.jquery.com/Plugins/Autocomplete/autocomplete#url_or_dataoptions

(vea la pestaña Opciones)


Sí, estoy al tanto de este complemento. Sin embargo, estamos utilizando jQueryUI en nuestra aplicación, por lo que sería bueno que esto funcione con el complemento de Autocompletar
jQueryUI

1
A partir del 2010-06-23, el complemento jQuery Autocomplete ha quedado en desuso en favor del complemento jQuery UI Autocomplete. Ver bassistance.de/jquery-plugins/jquery-plugin-autocomplete para obtener más información
Shek

0

Para admitir múltiples valores, simplemente agregue la siguiente función:

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>");
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.