Как я могу отформатировать результаты плагина автозаполнения?


167

Я использую плагин jQuery UI Autocomplete . Есть ли способ выделить последовательность символов поиска в раскрывающихся результатах?

Например, если у меня есть «foo bar» в качестве данных и я ввожу «foo», я получу « foo bar» в раскрывающемся меню, например:

«Завтрак» появляется после того, как «Бре» набрано: «Бре» выделено жирным шрифтом, а «Акфаст» - светлым.

Ответы:


233

Автозаполнение с живым предложением

Да, вы можете, если у вас есть автозаполнение.

В виджете автозаполнения, включенном в v1.8rc3 пользовательского интерфейса jQuery, всплывающее окно с подсказками создается в функции _renderMenu виджета автозаполнения. Эта функция определяется следующим образом:

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

Функция _renderItem определяется следующим образом:

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

Так что вам нужно заменить это _renderItem fn своим собственным созданием, которое производит желаемый эффект. Этот метод, переопределяющий внутреннюю функцию в библиотеке, я пришел к изучению, называется патч-аппликация . Вот как я это сделал:

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

Вызовите эту функцию один раз в $(document).ready(...) .

Это хак, потому что:

  • Для каждого элемента, представленного в списке, создается регулярное выражение obj. Это регулярное выражение obj должно быть повторно использовано для всех элементов.

  • нет класса css, используемого для форматирования законченной части. Это встроенный стиль.
    Это означает, что если бы у вас было несколько автозаполнений на одной странице, они все получили бы одинаковое обращение. Стиль CSS поможет решить эту проблему.

... но он иллюстрирует основную технику и работает для ваших основных требований.

альтернативный текст

обновленный рабочий пример: http://output.jsbin.com/qixaxinuhe


Чтобы сохранить регистр строк совпадения, в отличие от использования регистра напечатанных символов, используйте эту строку:

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

Другими словами, начиная с исходного кода выше, вам просто нужно заменить this.termна "$&".


РЕДАКТИРОВАТЬ
Вышеуказанное изменяет каждый виджет автозаполнения на странице. Если вы хотите изменить только один, посмотрите на этот вопрос:
Как установить * только один * экземпляр автозаполнения на странице?


Спасибо, Чизо. У вас есть ссылка на jsbin для этого?
dev.e.loper

1
Обратите внимание, что если вы делаете цепочку, важно сбросить контекст: oldFn.apply (this, [ul, item]);
Emanaton

Большое спасибо! Было бы здорово, если бы это стало частью jQuery UI.
Дэвид Райдер

4
Я бы сказал, что если вы хотите, чтобы он выделил жирным шрифтом результат в любой части совпадающей строки (не только в начале), измените строку RegExp следующим образом: var re = new RegExp (this.term);
Дэвид Райдер

1
По умолчанию автозаполнение JQueryUI выполняет поиск без учета регистра, поэтому имеет смысл добавить флаг «i» в объект RegExp. Также нет причин использовать «^» в регулярном выражении, как упоминалось @DavidRyder. Нравится: var re = new RegExp(this.term, "i"); Отличный пост!
Сэм

65

это также работает:

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

сочетание ответов @ Jörn Zaefferer и @ Cheeso.


Мне больше понравился этот, так как он соответствует целому слову.
АТП

3
Это хорошо работает. Единственное, на что нужно обратить внимание - это то, что item.label заменяется. Для меня я получил "<strong ..." в моем текстовом поле. Просто присвоив результат замены другой переменной, все прояснилось. У вас не будет этой проблемы, если ваши данные содержат свойство value, которое помещается в текстовое поле.
Киджана Вудард

это не работает, если у вас есть автозаполнение, которое поддерживает несколько значений. какие-либо предложения?
leora

Самый простой способ получить выделение текста в результатах автозаполнения. Очевидно, обратите внимание на ошибки, на которые указывали Леора и Киджана выше
adamst85

@RNKushwaha в любом месте, прежде чем позвонить$().autocomplete()
Брайан Лейшман

8

Супер полезно. Спасибо. +1.

Вот облегченная версия, которая сортирует слова «строка должна начинаться с термина»:

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
Благодаря Orolo, я использовал автозаполнение в нескольких местах и ​​хотел, чтобы центральное место, где я мог внести изменения, показывал только результат, начинающийся с напечатанных символов, и именно этот мне и был нужен!
dreamerkumar

Спасибо. Это лучшее решение из всех. Это работает для нечувствительных к регистру.
Аникет Кулькарни,

7

Вот он, функциональный полный пример:

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

Надеюсь это поможет


6

jQueryUI 1.9.0 меняет работу _renderItem.

Приведенный ниже код учитывает это изменение, а также показывает, как я выполнял сопоставление выделенных фрагментов с помощью плагина JQuery Autocomplete от Jörn Zaefferer. Он выделит все отдельные термины в общем поисковом запросе.

С тех пор как я перешел на использование Knockout и jqAuto, я обнаружил, что это гораздо более простой способ стилизации результатов.

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

Когда я выполняю поиск с такими символами, как '(', это вызывает ошибку ("Uncaught SyntaxError: Неверное регулярное выражение: / (sam | at | () /: Unterminated group"), чтобы решить эту проблему, предотвратив столкновение с регулярным выражением? "
Idan Shechter

Отличный ответ! Обожаю, что он выделяет термины независимо от того, где они появляются. Очень круто. Спасибо за обновление поста. Один вопрос, который у меня возник, касается используемого вами класса .ui-menu-item-highlight. Ожидается ли это определить jquery-ui или потребителями? Я изменил имя класса, чтобы приспособить мои собственные средства и просто сделал шрифт жирным шрифтом. .jqAutocompleteMatch { font-weight: bold; }
Сэм

@IdanShechter Отличный комментарий. Некоторая логика для выхода из this.termрегулярного выражения должна использоваться перед любой обработкой. См. Escape-строку для использования в регулярных выражениях Javascript как один из многих ответов на вопрос, как это сделать.
Сэм

3

для еще более простого способа попробуйте это:

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

Вот перефразировка решения Теда де Конинга. Оно включает :

  • Поиск без учета регистра
  • Нахождение многих вхождений искомой строки
$.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
Я думаю, что вы заново изобретаете колесо! Почему бы вам не использовать регулярные выражения, которые быстрее, проще и компактнее, чем весь этот код?
Джордж Маврицакис

2

Вот версия, которая не требует регулярных выражений и соответствует нескольким результатам в метке.

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

Это, вероятно, лучшее решение, но я считаю, что string.split может использовать только регистр символов.
Ноэль Абрахамс

У вас есть какой-нибудь способ сопоставления слов, основанный на регистре без учета регистра? Потому что, если у меня будет поиск "Альфа", он не будет выделять "Альфа"
Патрик


1

Вот моя версия:

  • Использует функции DOM вместо RegEx для разбиения строк / вставки тегов span
  • Затрагивается только указанное автозаполнение, не все
  • Работает с UI версии 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);
};

Пример выделенного текста


1

Вы можете использовать следующий код:

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

и логика:

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

он создает собственный виджет, который может переопределять _renderItemбез перезаписи _renderItemисходного прототипа плагина.

в моем примере также используется оригинальная функция рендеринга для упрощения кода

это важно, если вы хотите использовать плагин в разных местах с разным представлением автозаполнения и не хотите ломать свой код.


Обычно лучше тратить время на ответы на новые вопросы или на те, у которых нет ответов, а не на вопрос 6 лет, на который уже дан ответ.
zchrykng

0

Если вместо этого вы используете сторонний плагин, у него есть опция выделения: http://docs.jquery.com/Plugins/Autocomplete/autocomplete#url_or_dataoptions

(см. вкладку «Параметры»)


да, я знаю об этом плагине. однако мы используем jQueryUI в нашем приложении, поэтому было бы неплохо, чтобы это работало с плагином jQueryUI Autocomplete
dev.e.loper

1
По состоянию на 2010-06-23 плагин jQuery Autocomplete устарел в пользу плагина jQuery UI Autocomplete. См bassistance.de/jquery-plugins/jquery-plugin-autocomplete для получения дополнительной информации
ши

0

Чтобы поддерживать несколько значений, просто добавьте следующую функцию:

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>");
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.