Alex Rivera | Logout

Ajax sent on "keyup" duplicates results when fast typing!

Asked 2011-07-13T11:57:20.867
10

This is my Ajax:

$("form[0] :text").live("keyup", function(event) {

    event.preventDefault();
    $('.result').remove();
    var serchval = $("form[0] :text").val();

    if(serchval){

        $.ajax({

            type: "POST",
            url: "<?= site_url('pages/ajax_search') ?>",
            data: {company : serchval},
            success: function(data) {

                var results = (JSON.parse(data));
                console.log(results);

                if(results[0]){
                    $.each(results, function(index) {
                        console.log(results[index].name);
                        $("#sresults").append("<div class='result'>" + results[index].name + "</div>");
                    });
                }
                else {
                    $("#sresults").append("<div class='result'>לא נמצאו חברות</div>");
                }
            }
        });
    }
});

When I type slowly (slower then a letter per second) I get the results correct, when I type faster I get 2 times the same results

example:
slow typing: res1 res2 res3
fast typing: res1 res2 res3 res1 res2 res3

Also, any advice on improving the code would be welcome!

Edit
Report

1 Answer

9

Thats what is happening (pseudocode):

When you're typing slow:

.keyup1
.remove1
//asynchronous ajax1 request takes some time here...
.append1
.keyup2
.remove2
//asynchronous ajax2 request takes some time here...
.append2

When you're typing fast:

.keyup1
.remove1
//asynchronous ajax1 request takes some time here...
//and keyup2 happens before ajax1 is complete
.keyup2
.remove2
.append1
//asynchronous ajax2 request takes some time here...
.append2
//two results were appended _in a row_ - therefore duplicates

To solve duplicates problem, you would want to make your results removing/appending an atomic operation - using .replaceWith.

Build results HTML block first as string and then do the .replaceWith instead of .remove/.append:

var result = '';
for (i in results) {
    result += "<div class='result'>" + results[i].name + "</div>";
}

$("#sresults").replaceWith('<div id="sresults">' + result + '</div>');

Another problem (not related to duplicates) may be that older result overwrites newer which arrived earlier (because AJAX is asynchronous and server may issue responses not in the same order it receives requests).

One approach to avoid this is attaching roundtrip marker (kind of "serial number") to each request, and checking it in response:

//this is global counter, you should initialize it on page load, global scope
//it contains latest request "serial number"
var latestRequestNumber = 0;

$.ajax({
    type: "POST",
    url: "<?= site_url('pages/ajax_search') ?>",
    //now we're incrementing latestRequestNumber and sending it along with request
    data: {company : serchval, requestNumber: ++latestRequestNumber},
    success: function(data) {
        var results = (JSON.parse(data));
        //server should've
answered 2011-07-13T12:25:49.487

Your Answer