I implemented paging and filtering for page with list of products.
@model ProductFiltersViewModel
...
<div >
<aside >
<nav >
<ul id="nav_accordion">
<form asp-action="LoadGames" id="filters-form">
<input type="hidden" asp-for="PaginationInfo.PageNumber" />
<li >
<div >
<span>Page Size</span>
<select asp-for="PaginationInfo.PageSize" >
<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 id="loaded-games">
<div >
<table >
<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 >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 >
<div >
@if (item.Discontinued || item.UnitsInStock < 1)
{
<button disabled>Buy</button>
}
else
{
<button 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?
CodePudding user response:
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>
CodePudding user response:
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?
CodePudding user response:
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