10

How can you set a limit on the result from the jQuery autocomplete?

This is my code:

        $.ajax({
            url: "/cache/search/SearchModels.xml",
            dataType: "xml",
            success: function(xmlResponse) {
                var data = $("SearchModel", xmlResponse).map(function() {
                    return {
                        value: $("Name", this).text() + ", " + $("Description", this).text(),
                        id: $("No", this).text(),
                        name: $("Name", this).text(),
                        url: $("URL", this).text()
                    };
                }).get();
                $("#txtTopSearch").autocomplete({
                    source: data,
                    minLength: 2,
                    select: function(event, ui) {
                        BlockUI();
                        if (typeof (ui.item.url) != 'undefined') {
                            window.location = ui.item.url;
                        }
                        else {
                            alert('Page not found!');
                            $.unblockUI();
                        }
                    },
                    search: function(event, ui) {
                        $('#txtTopSearch').addClass('searchInProgress');
                    },
                    close: function(event, ui) {
                        $('#txtTopSearch').removeClass('searchInProgress');
                    }
                }).data("autocomplete")._renderItem = function(ul, item) {
                    return $("<li></li>")
                    .data("item.autocomplete", item)
                    .append("<a><span style='font-size:.9em; font-weight:bold;'>" + item.id + "</span><br /><span style='font-size:.8em;'>" + item.name + "</span></a>")
                    .appendTo(ul);
                };
            },
            error: function(xhr, textStatus, errorThrown) {
                alert('Error: ' + xhr.statusText);
            }
        });

This code return all results in the query, but I want to limit this to just showing 10 results. In the old autocomplete version there was an option for this, but it is now deprecated.

Example of the XML:

<?xml version="1.0"?>
<ArrayOfSearchModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <SearchModel>
    <No>1</No>
    <Name>My product</Name>
    <Description>My description</Description>
    <Tags>blue;brown;</Tags>
    <URL>/Products/1</URL>
  </SearchModel>
</ArrayOfSearchModel>
Martin at Mennt
  • 5,677
  • 13
  • 61
  • 89

6 Answers6

11

Final Update
after understanding that in my previous answers i was limiting the whole xml result set and not the results of the autocomplete

As you have overridden the default _renderItem method, you can override the default _renderMenu.

$.ui.autocomplete.prototype._renderMenu = function( ul, items ) {
   var self = this;
   $.each( items, function( index, item ) {
      if (index < 10) // here we define how many results to show
         {self._renderItem( ul, item );}
      });
}

answer is modified from this jQueryUI: how can I custom-format the Autocomplete plug-in results? so thanks go to @cheeso..


Original Answer

In you success callback use $("SearchModel:lt(10)", xmlResponse).map(...

The :lt(10) gets elements with an index of less than 10. So max 10 results will be returned..

(of course the number 10 could be anything you want)

Look at :lt() selector at http://api.jquery.com/lt-selector/

update

Although i cannot understand why the first answer does not work, since the SearchModel is a tag and we target that.. we can move the filtering in the map method..

success: function(xmlResponse) {
                var data = $("SearchModel", xmlResponse).map(function(index) {
                    if (index<10)
                      {
                        return {
                            value: $("Name", this).text() + ", " + $("Description", this).text(),
                            id: $("No", this).text(),
                            name: $("Name", this).text(),
                            url: $("URL", this).text()
                               };
                      }
                      else
                      { return null; }
                }).get();

documentation of map()

Community
  • 1
  • 1
Gabriele Petrioli
  • 191,379
  • 34
  • 261
  • 317
  • Promising suggestion, but it did not work. It dont look like `SearchModel` is a regular selector. – Martin at Mennt Nov 01 '10 at 18:54
  • @Martin, i assumed it was a tag used inside the xml. Can you post some content from the XML file ? i will post an alternative solutions using the `map` method – Gabriele Petrioli Nov 01 '10 at 19:03
  • Thank you, I have updated my question with an example of an node. – Martin at Mennt Nov 01 '10 at 19:07
  • Thank you, but this would actually limit the XML data source and just return the first 10 results in the XML file. And that is even before you start searching. The xml file need to be loaded in full extent. – Martin at Mennt Nov 01 '10 at 19:33
  • @martin .. i see the light now .. both my cases (*the first works as well in that regard*) work on the initial data and not on the results after the autocomplete filtering ... looking some more .. – Gabriele Petrioli Nov 01 '10 at 19:41
  • This is the solution I was searching for, than you very much :) – MarioRicalde May 04 '11 at 04:40
3

Why not limit the data that your query returns from your xml source?

You have to remember that the autocomplete functionality is essentially using a regex to match items in the datasource. Having a large datasource is bad because it has to parse so much data simply to find the correct item.

desertnaut
  • 57,590
  • 26
  • 140
  • 166
Achilles
  • 11,165
  • 9
  • 62
  • 113
  • Sure, I would love to do that, but how? I have looked for limitation options in the jQuery `.map()` and `.get()` but have not found any, im kinda stuck. – Martin at Mennt Nov 01 '10 at 18:38
  • Reduce the size of the xml file. If you aren't interested in showing all the results that are being matched in the file then reduce the size of the file(the number of things in it). – Achilles Nov 01 '10 at 18:40
  • Fair enough, but are there not any other ways to do this without changing the XML file? – Martin at Mennt Nov 01 '10 at 18:46
  • 1
    @Martin, Achilles is correct, why would you want to return 100 xml entries, only to show 10. Makes sense to limit to 10 on the xml generation side (mysql/sql/etc) – Jakub Nov 01 '10 at 19:11
  • Well, I get results from an ERP system which has an slow integration. So instead of searching directly in the ERP system I make a cache XML file once a day. This cahce file includes all the products from the ERP system. And searching in this file is very fast, but searching directly in the ERP system is very slow. – Martin at Mennt Nov 01 '10 at 19:18
  • No I understand your approach, the problem you are running into is that 1. The performance will suck because the data is being evaluated against a regular expression to find each item that matches. 2. You are putting the browser through the effort of processing data that isn't really being used and that's not the idea way to query against a large dataset. – Achilles Nov 01 '10 at 20:46
2

This is what I did based on some reading of the autocomplete API docs

$input.autocomplete({
  source: function( request, response ) {
    $.getJSON('search.php', request, function( data, status, xhr ) {
      // return a max of 10 results.
      response( data.slice(0, 9) );
    });
  }
})

Following this pattern saves having to do any funky stuff in the render layer.

Paul Sheldrake
  • 7,505
  • 10
  • 38
  • 50
2

The quickest way to limit results is doing it during the "open" event. We can delete part of the content dynamically created by jquery ui, reducing the children element array.

This solution solved the same issue for me:

var maxResults = 10;
$input.autocomplete({
    source: somefile.json,
    open: function(event,ui){
          $(this).data("uiAutocomplete").menu.element.children().slice(maxResults).remove();
         }
})
1

You could add a handler for the "open" event:

open:function(event,ui){ 
var maxListLength=10;
var ul = jQuery( "#txtTopSearch" ).autocomplete("widget")[0];
while(ul.childNodes.length > maxListLength)
      ul.removeChild(ul.childNodes[ul.childNodes.length-1]);
}
desertnaut
  • 57,590
  • 26
  • 140
  • 166
sorry
  • 11
  • 2
0

I've got a clue for WordPress.

Check a file like assets/js/custom.js, if you have it, for:

$('input[name=ad_title]').typeahead({
        minLength: 1,
        delay: 250,
        scrollBar: true,
        autoSelect: true,
        fitToElement: true,
        highlight: false,
        hint: true,
        source: function (query, process) {
            return $.get(ajax_url, {query: query, action: 'fetch_suggestions'}, function (data) {
                jQuery('.adforest-search-spinner').remove();

                data = $.parseJSON(data);
                return process(data);
            });
        }

and change minLenght for more letters or add maxResults.

Destroy666
  • 892
  • 12
  • 19
Maciek
  • 1
  • 1