skip to Main Content

I implemented paging and filtering for page with list of products.

@model ProductFiltersViewModel

...

<div class="row">
    <aside class="col-3">
        <nav class="sidebar card py-2 mb-4">
            <ul class="nav flex-column" id="nav_accordion">
                <form asp-action="LoadGames" id="filters-form">
                
                    <input type="hidden" asp-for="PaginationInfo.PageNumber" />
                    <li class="nav-item">
                        <div class="container-fluid">
                            <span>Page Size</span>
                            <select asp-for="PaginationInfo.PageSize" class="form-select">
                                <option value="2" selected>2</option>
                                <option value="10">10</option>
                                <option value="20">20</option>
                                <option value="50">50</option>
                                <option value="100">100</option>
                            </select>
                        </div>
                    </li>
//Filters here not important for the question//
                </form>

            </ul>
        </nav>
    </aside>
    <div class="col-9" id="loaded-games">
        <div class="table-responsive">
            <table class="table table-striped">
                <thead>
                <tr>
                    <th>Name</th>
                    <th>Placed</th>
                    <th>Views</th>
                    <th>Price</th>
                    <th></th>
                </tr>
                </thead>
                <tbody id="loaded-games-rows">
                </tbody>
            </table>
        </div>

        <div id="paging-control" style="display: none;">
            <button class="btn btn-success btn-load-games">Load more</button>
        </div>
    </div>
</div>

@section Scripts
{
    <script type="text/javascript" src="js/LoadGames.js"></script>
    ...
}

So when i click "load more" button jquery do ajax request, get certain amount of products (partial view) and place them at the end of the page.

$(document).ready(function () {
    loadGames($("form#filters-form"), true);

    $(".btn-load-games").click(function () {
        var form = $("form#filters-form");
        loadGames(form);
    });

    function loadGames(form, isInitial = false) {
        $.ajax({
            type: "POST",
            url: "/games",
            data: form.serialize(),
            success: function (response) {
                $("tbody#loaded-games-rows").append(response);
                incrementPageNumber();

                if (isInitial && !checkAndDisplayMessageIfNoGamesFound(response)) {
                    $("div#paging-control").show();
                }

                checkIfAllSuitedGamesAlreadyLoaded(response);
            },
            error: function (response) {
                alert(response.responseText);
            }
        });
    }
});

this partial view contains raws of products and every raw has a button "buy".

@model List<ProductDetailsViewModel>
@foreach (var item in Model)
{
    <tr>
        ...
        <td class="align-middle"> 
            <div class="d-flex justify-content-end">
                @if (item.Discontinued || item.UnitsInStock < 1)
                {
                    <button class="btn-add-to-basket btn btn-success" disabled>Buy</button>
                }
                else
                {
                    <button class="btn-add-to-basket btn btn-success" gamekey="@item.Key">Buy</button>
                }
                ...
            </div>
        </td>
    </tr>
}

<script type="text/javascript" src="js/AddGameToBasket.js"></script>

Script with jquery is attached to this partial view which send an ajax request, and add product to basket on buy button click. (BTW I can’t attach this script to the main view because products are not loaded yet and there are no "buy" buttons on the DOM model so when i click button nothing hapens).

$(document).ready(function () {
    $(".btn-add-to-basket").click(function () {
        var gamekey = $(this).attr("gamekey");

        $.ajax({
            type: "POST",
            url: "/game/" + gamekey + "/buy",
            data: gamekey,
            success: function (response) {
                alert("Added to basket");
            },
            error: function (response) {
                alert(response.responseText);
            }
        });
    });
});

The issue is when another bunch of products are loaded previous buttons also begin listen to the event and when i click "buy" on the initialy loaded games event called twice and i end up with multiple requests to the server. So what can I do?

3

Answers


  1. Chosen as BEST ANSWER

    I've just remembered that i was warned about this issue before and the solution is to use this in the "main" page

    $(document).on("click", ".btn-add-to-basket", function () {
            ... your code here
        });
    

    instead of $(.btn-add-to-basket).click( function () {...} ); in partial view

    P.S. Any suggestions on the clearer question's title for people who face this problem in the future?


  2. When you add dinamically elements to the DOM, you need to atach them the eventlistener, can be done in the creation time to avoid the double event attachament, here its an example

    $('#noevent').click(function(){
      let btn=document.createElement('button');
      btn.textContent ='No';
      $('.container').append(btn)
    })
    
    $('#event').click(function(){
      let btn=document.createElement('button');
      btn.textContent ='Yes';
      
      //The new event listener
      $(btn).click(function(){
          console.log('YES!!')
      })
      
      $('.container').append(btn)
    })
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    
    <button id='noevent'>No event</button><button id='event'>With event</button>
    <div class='container'></div>
    Login or Signup to reply.
  3. I think that your best approach will be event-delegation.

    jQuery Docs – .on() method

    Example:

    $( "#dataTable tbody" ).on( "click", "tr", function() {console.log( $( this).text() );});
    

    Better "listen" not to the document object but to container that wraps your dynamic elements.

    Also you can learn from here how it works in JS:
    Event Delegation in JavaScript

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search