0

I have been reading a lot of answers on here about deferred techniques on getJSON/ajax requests but nothing seems to fit my scenario. I am making a search input return a set of results based on a getJSON call after 4 characters. This is to limit the amount of results returned, and also the amount of ajax requests (open to more effective solutions).

search_input.on('keyup', function() {

    var string       = $(this).val();
    var quiet_chars  = 4;

    if(string.length < quiet_chars) {

        // Simple notifications function which passes a message to a div, and optional class
        notification('Type '+(quiet_chars - string.length)+' more characters to search');
    }
    if(string.length == quiet_chars) {
        notification('Searching...', 'loading');
        $.getJSON('stores/stores-search/'+string, function(data) {

            // Loop through data and build list....
            $('.search_results').html('<ul>'+list_html+'</ul>');

            // Function which shows/hides list items based on Jquery :contains
            searchResults(string);

        });
    }
    if(string.length > quiet_chars) {
        searchResults(string);
    }

});

The specific problem I am facing is if you quickly type in 5 (more than 4) characters, the if(string.length > quiet_chars) condition is met before $('.search_results').html('<ul>'+list_html+'</ul>'); has executed, and tells the user there are no search results.

I need to meet the if(string.length > quiet_chars) condition to continue filtering the returned results, but only after the list has been appended to the DOM from the getJSON request. Thanks.

daviestar
  • 4,531
  • 3
  • 29
  • 47

2 Answers2

2

edited to fit your specifications

bool dataLoaded = false; //this is a global variable we have outside of the scope of the search_input.on function
search_input.on('keyup', function() {

    var string       = $(this).val();
    var quiet_chars  = 4;

    if(string.length < quiet_chars) {
        //let's reset the dataLoaded flag if the user ends up deleting characters and what not
        if(dataLoaded)
           dataLoaded = !dataLoaded;
        // Simple notifications function which passes a message to a div, and optional class
        notification('Type '+(quiet_chars - string.length)+' more characters to search');
    }
    if(string.length == quiet_chars) {
        notification('Searching...', 'loading');
        $.getJSON('stores/stores-search/'+string, function(data) {

            // Loop through data and build list....
            $('.search_results').html('<ul>'+list_html+'</ul>');
            dataLoaded = true;
            // Function which shows/hides list items based on Jquery :contains
            searchResults(string);

        });
    }
    else if(dataLoaded){
        searchResults(string);
    }


});
mrpanda
  • 104
  • 5
  • I tried a few different tricks using >=. Although this will work, it will make an ajax request on each keyup which I'm trying to avoid. It also renders my nice searchResults function kinda useless. – daviestar Feb 27 '14 at 01:41
  • o snaps, I see what you're getting at, let me revise the code for you – mrpanda Feb 27 '14 at 03:21
  • What happens now is the `else if(dataLoaded)` condition isn't met by 5 characters, no results are displayed, and the notification continues to say 'Type 1 more characters to search'. I think I need to create a listener/deferred type function inside `else if(dataLoaded)` which will only run when the getJSON has finished. Any ideas? – daviestar Feb 27 '14 at 04:10
  • Thanks for your help mrpanda! I have posted my tested and working solution. Cheers. – daviestar Feb 27 '14 at 05:03
0

Got it working!

var json_success = false,
    ajax_term;

function searchJson(string) {
    ajax_term = string;
    notification('Searching...', 'loading');
    $.getJSON('stores/stores-search/'+string, function(data) {

        // Loop through data and build list....
        $('.search_results').html('<ul>'+list_html+'</ul>');
        searchResults(string);
        json_success = true; 

    });
}

search_input.on('keyup', function() {

    var string       = $(this).val();
    var quiet_chars  = 4;

    if(string.length < quiet_chars) {
        json_success = false;
        notification('Type '+(quiet_chars - string.length)+' more characters to search');
    } else {

        if(json_success !== false || string.substr(0,4) !== ajax_term) {
            $.when(searchJson(string)).then(searchResults(string))
        } else {
            searchResults(string)
        }
    }
});

From here: https://stackoverflow.com/questions/5280699#5291067

If anyone is trying to get something similar working, here is my search results function:

//extends :contains to be case insensitive
$.extend($.expr[':'], {
    'containsi': function(elem, i, match, array) {
        return (elem.textContent || elem.innerText || '').toLowerCase().indexOf((match[3] || "").toLowerCase()) >= 0;
    }
});

// Instant search results
function searchResults(searchTerm) {

    var searchSplit = searchTerm.replace(/ /g, "'):containsi('");

    $(".search_results .tile").not(":containsi('" + searchSplit + "')").each(function() {
        $(this).addClass('hide').removeClass('show');
    });
    $(".search_results .tile:containsi('" + searchSplit + "')").each(function() {
        $(this).removeClass('hide').addClass('show');
    });

    var resultCount = $('.search_results .show').length;

    if(resultCount != '0') {
        notification('Showing '+resultCount+' results');
    } else {
        notification('No results', 'error');
    }
}

Credit for the case-insensitive version of :contains goes to Rob Sawyer.

Community
  • 1
  • 1
daviestar
  • 4,531
  • 3
  • 29
  • 47
  • Doesn't look quite right to me. `json_success === '1'` will suppress will suppress further `getJSON` calls such that the cached results can be searched; but what if the user deletes one or more characters then types something else? You need a "deletion detection" mechanism, which sets `json_success` back to `false`. – Beetroot-Beetroot Feb 28 '14 at 04:17
  • @Beetroot-Beetroot I just came back here for that very reason :) Answer updated, thanks for looking. – daviestar Feb 28 '14 at 05:06
  • Needs more. *Any* deletion should trigger `json_success = false`, not just when the length is below the `quiet_chars` threshold. – Beetroot-Beetroot Feb 28 '14 at 10:48
  • No, I return all results which include the 4-character string, and anything after is filtered by my searchResults function. If the user has typed 7 characters and deletes 2, I already have those results downloaded. My db only contains around 2000 stores, and from testing 4 characters will return no more than 79-80 stores, it works for my case, and seems very fast. – daviestar Mar 01 '14 at 02:12
  • Actually I see your point if the user deletes characters from the start of the string :) Will have a look – daviestar Mar 01 '14 at 02:13
  • Exactly, a deletion can be from anywhere in the string, not just the right hand end. The simplest strategy, though not the most efficient, is to assume that new results are required every time a deletion is detected. – Beetroot-Beetroot Mar 01 '14 at 03:13
  • I think I'll use a solution that compares the first 4 chars each time. Will post back on Monday. Cheers. – daviestar Mar 01 '14 at 06:22
  • Yes, that is the slightly more difficult but better approach. – Beetroot-Beetroot Mar 01 '14 at 14:05