1

I have a text field where, as the user types, AJAX requests are sent to the server for a search to be performed and results returned:

<input type="text" class="form-control player-list-control-text" id="name" name="name" autocomplete="off" />

$('.player-list-control-text').keyup(function(){
    get_player_list();
});

function get_player_list(){
    $.ajax({
        url: '/player/list/get',
        data: {
            'club_id' : $('#club_id').val(),
            'position_id' : $('#position_id').val(),
            'name' : $('#name').val(),
            'page' : players_page,
            'sort_by' : sort_by,
            'sort_order' : sort_order,
        },
        type: 'POST',
        success: function(result) {
            if(result.hasOwnProperty('error'))
                show_msg('error', result.error);
            else
                load_players(result);
        },
        error: function (response) {
            handle_error(response);
        }
    });
}

The issue is, the search takes a second or two to execute, so when the user quickly types, say, 5 characters, the requests get all jumbled up and it either takes a very long time or it shows the wrong results - for example, the results of the 4-letter search instead of the results of the 5-letter search.

I could make sure the results of the last request sent are displayed by saving the request times on the client or preventing the user from typing while the previous request is being processed, but both seem like bad solutions - the first because it doesn't actually save the server any trouble and the second for the bad user experience.

What I'm really looking for is a way to interrupt the obsolete 4-letter search mid-execution on the server when the new, 5-letter search query is sent from the client.

Is this possible? How would I go about it?

The back-end script contains multiple queries since we have to search multiple tables if that's relevant at all.

EDIT: regarding the similar question that prompted a close vote, the solution there is not what I'm after. I want to interrupt server-side processing, not merely stop the request on the client.

sveti petar
  • 3,637
  • 13
  • 67
  • 144

2 Answers2

2

Delay the request till the user finishes typing

var timer;
$('.player-list-control-text').keyup(function(){
    clearTimeout(timer);
    timer = setTimeout(function () {get_player_list();}, 500);
});

500ms saves you a lot of useless requests on a search input.

N69S
  • 16,110
  • 3
  • 22
  • 36
2

Use debounce

lodash's debounce instead of writing from scratch yourself.

const debouncedGetPlayerList = _.debounce(get_player_list, 2000);

$('.player-list-control-text').keyup(function(){
    debouncedGetPlayerList(); // execute get_player_list after 2s non-interupted
});

Live example: https://codepen.io/dcorb/pen/mVGVOL

$(document).ready(function(){

  var $statusKey = $('.status-key');
  var $statusAjax = $('.status-ajax');
  var intervalId;
  
  // Fake ajax request. Just for demo
  function make_ajax_request(e){
      var that = this;
      $statusAjax.html('That\'s enough waiting. Making now the ajax request');
     
      intervalId = setTimeout(function(){
         $statusKey.html('Type here. I will detect when you stop typing');
         $statusAjax.html('');
         $(that).val(''); // empty field
      },2000);
    }
  
  // Event handlers to show information when events are being emitted
    $('.autocomplete')
      .on('keydown', function (){  
      
        $statusKey.html('Waiting for more keystrokes... ');
      clearInterval(intervalId);             
      })
     
  
  // Display when the ajax request will happen (after user stops typing)
    // Exagerated value of 1.2 seconds for demo purposes, but in a real example would be better from 50ms to 200ms
  $('.autocomplete').on('keydown',
       _.debounce(make_ajax_request, 1300));
  });
body {
   background: #444444;
   color: white;
   font: 15px/1.51 system, -apple-system, ".SFNSText-Regular", "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", "Lucida Grande", sans-serif;
   margin:0 auto;
   max-width:800px;
   padding:20px;
}

form {
  display: inline-block;
  padding: 0;
  margin: 0;
  padding: 5px;
  margin: 5px 0 0 0;
}

input {
  padding:8px 20px;
  border-radius: 2px;
  border:0;
  font-size:20px;
}
.status-key,
.status-ajax {
  margin:10px 0;
}
.status-ajax {
  color:#99FF7E;
}
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>


<form>
  <div class="status-key">Type here. I will detect when you stop typing</div>
  <input type="text" class="autocomplete">
  <div class="status-ajax"></div>
</form>
Loi Nguyen Huynh
  • 8,492
  • 2
  • 29
  • 52
  • It does exactly what i proposed below (in this case) without having to load another library. – N69S Dec 25 '19 at 15:18