Skip to content
Advertisement

If buttons of item were inactive for 2 sec – make action

I would like to implement such logic:

  • User clicks on “Plus” or “Minus” button.
  • If the user don’t click at any of those buttons for 2 seconds, then we assume that current quantity should be sent to the server.

Right now, I have three buttons: “Plus” – increments quantity by 1 and changes the value at page. “Minus” – decreases quantity by 1 and changes the value at page. “Confirm” – The button that sends request with current quantity parameter to Spring Boot controller and changes quantity on server side. I would prefer to avoid this button, because it adds complexity.

Is there a convenient way to get rid of the confirm button? The only way I know how we can do this is by sending request to controller on each “Plus” or “Minus” button click. But it seems that this approach will be inefficient.

$(document).ready(function () {
  //include csrf for every ajax call
  $(function () {
    let token = $("meta[name='_csrf']").attr("content");
    let header = $("meta[name='_csrf_header']").attr("content");
    $(document).ajaxSend(function (event, xhr, options) {
      xhr.setRequestHeader(header, token);
    });
  });

  $(".plusForm").submit(function (event) {
    event.preventDefault();
    let $prodCount = $(this).parent().parent().parent().find(".prodCount span");
    let currentQuantity = parseInt($prodCount.text());
    $prodCount.text(++currentQuantity);
  });

  $(".minusForm").submit(function (event) {
    event.preventDefault();
    let $prodCount = $(this).parent().parent().parent().find(".prodCount span");
    let currentQuantity = parseInt($prodCount.text());
    $prodCount.text(--currentQuantity);
  });

  $(".changedQuantityForm").submit(function (event) {
    event.preventDefault();
    let $prodCount = $(this).parent().parent().parent().find(".prodCount span");
    let quantity = parseInt($prodCount.text());
    let productId = $(this).parent().parent().parent().parent().find(
        '.product-id').val();
    changeQuantityAjax(productId, quantity);
  });
  
  function changeQuantityAjax(id, quantity) {
  console.log("quantity changed on server side");
    /* $.ajax({
      type: "PUT",
      contentType: "application/json",
      url: "/rest/cart/" + id + "?quantity=" + quantity,
      data: {
        "quantity": quantity
      },
      success: function () {
        console.log('SUCCESS ' + id + ' ' + quantity);
        // alert(name + ' was deleted')
      },
      error: function () {
        console.log('ERROR ' + id + ' ' + quantity);
      }
    }); */
  }
})
  <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css"
        rel="stylesheet" media="screen"/>
  <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css">
  
<div class="row justify-content-center">
  <div class="col mb-4 prodItem">
    <div class="card border-primary" style="height: 34rem; width: 26rem;">
      <div class="view overlay">
        <img class="card-img-top img-fluid"
             src="https://cdn.pixabay.com/photo/2018/10/05/23/24/chicken-3727097_1280.jpg"
             style="height : 18rem;" alt="Card image cap">
        <div class="mask rgba-white-slight"></div>
      </div>

      <div class="card-body">
        <h3><span>Chicken</span></h3>
        <div class="float-right">
          <h2 class="card-title"><span>1000</span> $</h2>
        </div>

        <br>
        <br>
        <br>

        <div class="form-control">
          <div class="row prodCount" style="margin: auto; font-size: 17px">
            <p> In cart : &nbsp</p>
            <span>0</span>


            <div class="row float-right" style="margin: auto">
              <form class="plusForm">
                <button type="submit" class="btn-sm">
                  <i class="fas fa-plus-circle fa-w-16 fa-3x text-danger"></i>
                </button>
              </form>

              <form class="minusForm">
                <button type="submit" class="btn-sm">
                  <i class="fa fa-minus-circle fa-w-16 fa-3x" aria-hidden="true"></i>
                </button>
              </form>

              <form class="changedQuantityForm">
                <button type="submit" class="btn-sm">
                  <i class="fas fa-check fa-w-16 fa-3x text-success"></i>
                </button>
              </form>
            </div>
          </div>

        </div>

        <br>
      </div> <!-- card-body -->
    </div> <!-- card -->
  </div>

Fiddle

PC: It seems that my question is related to this topic(https://stackoverflow.com/a/7762539/14308420). But I’m not sure if I should apply it in my case and how to do it.

I extracted the logic of submit button to function updateQuantityOnServer(button). And after adding this line:

changedQuantityTimeout = setTimeout(updateQuantityOnServer(this), 1000);

I got a warning:

Argument type void is not assignable to parameter type TimerHandler Type void is not assignable to type string | Function Type void is not assignable to type Function.

As I understand, it’s caused because I send button as parameter. But I use this button to get parameters…

Advertisement

Answer

Look at the changeQuantityAjax function for the implementation of clearTimeout and setTimeout together, making a “minimal” delay of 2 seconds after the last user action.

From each button click, this was passed to the getIdandQuantity function.

It does not change much things in the logic, but notice the .parents() instead of parent().parent().parent().

$(document).ready(function() {
  //include csrf for every ajax call
  $(function() {
    let token = $("meta[name='_csrf']").attr("content");
    let header = $("meta[name='_csrf_header']").attr("content");
    $(document).ajaxSend(function(event, xhr, options) {
      xhr.setRequestHeader(header, token);
    });
  });

  $(".plusForm").submit(function(event) {
    event.preventDefault();
    let $prodCount = $(this).parents(".prodCount").find("span");
    let currentQuantity = parseInt($prodCount.text());
    $prodCount.text(++currentQuantity);
    getIdandQuantity(this);
  });

  $(".minusForm").submit(function(event) {
    event.preventDefault();
    let $prodCount = $(this).parents(".prodCount").find("span");
    let currentQuantity = parseInt($prodCount.text());
    $prodCount.text(--currentQuantity);
    getIdandQuantity(this);
  });

  // That is the function to retreive the id and quantity from the clicked button
  function getIdandQuantity(btn) {
    let $parent = $(btn).parents(".prodCount");
    let quantity = parseInt($parent.find("span").text());
    let productId = $parent.find('.product-id').val();
    changeQuantityAjax(productId, quantity);
  }

  // A variable to store the refence to the pending setTimeout
  let ajaxTimeout

  function changeQuantityAjax(id, quantity) {
  
    // Clear any existing setTimeout
    clearTimeout(ajaxTimeout)
    
    // Set a 2 seconds timeout
    ajaxTimeout = setTimeout(function() {

      console.log("quantity changed on server side");
      /* $.ajax({...}) */

    }, 2000)
  }
})
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" media="screen" />
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css">

<div class="row justify-content-center">
  <div class="col mb-4 prodItem">
    <div class="card border-primary" style="height: 34rem; width: 26rem;">
      <div class="view overlay">
        <img class="card-img-top img-fluid" src="https://cdn.pixabay.com/photo/2018/10/05/23/24/chicken-3727097_1280.jpg" style="height : 18rem;" alt="Card image cap">
        <div class="mask rgba-white-slight"></div>
      </div>

      <div class="card-body">
        <h3><span>Chicken</span></h3>
        <div class="float-right">
          <h2 class="card-title"><span>1000</span> $</h2>
        </div>

        <br>
        <br>
        <br>

        <div class="form-control">
          <div class="row prodCount" style="margin: auto; font-size: 17px">
            <p> In cart : &nbsp</p>
            <span>0</span>


            <div class="row float-right" style="margin: auto">
              <form class="plusForm">
                <button type="submit" class="btn-sm">
                  <i class="fas fa-plus-circle fa-w-16 fa-3x text-danger"></i>
                </button>
              </form>

              <form class="minusForm">
                <button type="submit" class="btn-sm">
                  <i class="fa fa-minus-circle fa-w-16 fa-3x" aria-hidden="true"></i>
                </button>
              </form>

            </div>
          </div>

        </div>

        <br>
      </div>
      <!-- card-body -->
    </div>
    <!-- card -->
  </div>

Additionnally! You could easilly merge the 4 functions above (2 click handlers, getIdandQuantity and changeQuantityAjax) into one single click handler.

$(document).ready(function() {
  //include csrf for every ajax call
  $(function() {
    let token = $("meta[name='_csrf']").attr("content");
    let header = $("meta[name='_csrf_header']").attr("content");
    $(document).ajaxSend(function(event, xhr, options) {
      xhr.setRequestHeader(header, token);
    });
  });

  // A variable to store the refence to the pending setTimeout
  let ajaxTimeout
  
  $(".plusForm, .minusForm").submit(function(event) {
    event.preventDefault();
    
    // Get the elements needed, the product is and the quantity shown
    let $parent = $(this).parents(".prodCount");
    let id = $parent.find('.product-id').val();
    let $prodCount = $parent.find("span")
    let currentQuantity = parseInt($prodCount.text());
    
    // Increment OR decrement the quantity
    let quantity = ($(this).hasClass("plusForm")) ? ++currentQuantity : --currentQuantity
    
    // Update the shown quantity
    $prodCount.text(quantity)
    
    // Clear any existing setTimeout
    clearTimeout(ajaxTimeout)
    
    // Set a 2 seconds timeout
    ajaxTimeout = setTimeout(function() {

      console.log("quantity changed on server side");
      /* $.ajax({...}) */
      
    }, 2000)
  });

})
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" media="screen" />
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css">

<div class="row justify-content-center">
  <div class="col mb-4 prodItem">
    <div class="card border-primary" style="height: 34rem; width: 26rem;">
      <div class="view overlay">
        <img class="card-img-top img-fluid" src="https://cdn.pixabay.com/photo/2018/10/05/23/24/chicken-3727097_1280.jpg" style="height : 18rem;" alt="Card image cap">
        <div class="mask rgba-white-slight"></div>
      </div>

      <div class="card-body">
        <h3><span>Chicken</span></h3>
        <div class="float-right">
          <h2 class="card-title"><span>1000</span> $</h2>
        </div>

        <br>
        <br>
        <br>

        <div class="form-control">
          <div class="row prodCount" style="margin: auto; font-size: 17px">
            <p> In cart : &nbsp</p>
            <span>0</span>


            <div class="row float-right" style="margin: auto">
              <form class="plusForm">
                <button type="submit" class="btn-sm">
                  <i class="fas fa-plus-circle fa-w-16 fa-3x text-danger"></i>
                </button>
              </form>

              <form class="minusForm">
                <button type="submit" class="btn-sm">
                  <i class="fa fa-minus-circle fa-w-16 fa-3x" aria-hidden="true"></i>
                </button>
              </form>

            </div>
          </div>

        </div>

        <br>
      </div>
      <!-- card-body -->
    </div>
    <!-- card -->
  </div>
Advertisement