How to prevent spamming an ajax call when using arrows inside input type number

Viewed 256

I've got a function that triggers an ajax call on change of an input with type number. When I click the arrows inside the input field and keep my finger on the click button, the number increases/decreases fast but only sends the ajax call after I release my finger. This is great.

The problem is when I focus inside the input and use my arrow keys on my keyboard to increase or decrease the input, my code sends an ajax call for every time the number changes, which can be alot. This means my ajax calls break because too many are sent in a short amount of time.

How can I get the same behaviour like when clicking the arrows inside the input type for when I press the arrow buttons on my keyboard?

My code:

HTML

<div class="prodinfoquantity">
    <input class="productid" type="hidden" name="productid" value="60b0f82b46193" />
    <input type="number" class="form-control aantal" value="10" min="1" />
</div>

My jquery

$('.checkoutwrap .aantal').on("change", function(e) {
  e.preventDefault();
    var productid = $(this).siblings('.productid').val();
    var quantity = $(this).val();
  var form_data = $("#formsid form").serialize();
  $.ajax({
    type:'post',
    url:"checkout/prices.php",
    data:({productid: productid, quantity: quantity}),
    success:function(data){
        $($pricediv).empty().append( data );
        refreshcoupon(true);
    }
  });
});

This is what it looks like if I use my keyboard:

enter image description here

I tried using keyup instead of change but then it stops sending an ajax call when I click the arrow buttons that are inside the input field.

2 Answers

What you want is known as throttling or debouncing.

Here is a simplified version

const DELAY = 300;// adjust to suit needs

$('.aantal').on("change", function(e) {
 
  let $input = $(this),timer = $input.data('timer');
  // clear previous timer
  clearTimeout(timer);
  // set new one
  timer = setTimeout(doStuff, DELAY);
  $input.data('timer', timer)

  function doStuff() {
    console.log('Do stuff');
    $input.data('timer', null)
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="prodinfoquantity">
  <input class="productid" type="hidden" name="productid" value="60b0f82b46193" />
  <input type="number" class="form-control aantal" value="10" min="1" />
</div>

Using the the following function from one of the comments above:

function debounce(func, wait, immediate) {
    var timeout;
    return function() {
        var context = this, args = arguments;
        var later = function() {
            timeout = null;
            if (!immediate) func.apply(context, args);
        };
        var callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
    };
};

You can then apply it to your on change function like this:

$('.checkoutwrap .aantal').on("change", debounce(function(e) {
    e.preventDefault();
    var productid = $(this).siblings('.productid').val();
    var quantity = $(this).val();

    var form_data = $("#formsid form").serialize();
    $.ajax({
        type:'post',
        url:"checkout/prices.php",
        data:({productid: productid, quantity: quantity}),
        success:function(data){
            $($pricediv).empty().append( data );
            refreshcoupon(true);
        }
    });
}, 300)) ;
Related