I have a EJS template which gets included on a page by running a for loop. Each template has a link which when clicked should toggle the display/hiding of a number of checkboxes. I am trying to use AJAX and have a javascript file which is linked to the template. In this javascript file I add an eventlistener to the link on that template. However only the link on the first template on the page fires.
The EJS template:
<link rel="stylesheet" href="../css/categorieslist.css">
<article class="post-item" id="<%= user.UserID %>" data-userId="<%= user.UserID %>">
<p>
<%= user.Email %>
<input type="hidden" class="hiddenField" id="userid" name="userid" value="<%= user.UserID %>" required>
<p><%= user.UserID %></p>
<a class="btn catbtn" href="/admin-usercategories/<%= user.UserID %>/edit">Edit Call</a>
<div class="categorieslist">
<% for (const cat of categories) { %>
<p>
<input type="checkbox" id="<%= cat.CategoryID %>" value="<%= cat.CategoryID %>">
<label for="<%=cat.CategoryID %>"><%= cat.CategoryName %></label>
</p>
<% } %>
</div>
</p>
</article>
<script type="text/javascript" src="/javascript/usercategories.js" charset="utf-8"></script>
The javascript file:
const btn = document.querySelector(".catbtn");
const displayElement = document.querySelector(".categorieslist");
btn.addEventListener('click', function() {
fetchUserCategories();
});
async function fetchUserCategories() {
if (displayElement.style.display === "none") {
displayElement.style.display = "block";
} else {
displayElement.style.display = "none";
}
}
2
Answers
Because a click handler is only being attached to one element. This finds an element:
Even the variable name implies this. And then the usage of the variable indicates that it’s just one element:
If you want to find multiple elements, you want
querySelectorAll
:Then loop over the results to attach the click handlers:
Of course, this will lead to the next problem… Every button does exactly the same thing. It "fetches user categories" on the first
displayElement
. You probably want the immediate sibling element of the one which was clicked. For example, you might pass thenextElementSibling
to the function:And then in
fetchUserCategories
use that element reference:If the markup could change then you might want to try other approaches to get the sibling element. For example:
Techniques often include starting from the current element (
this
), traversing up the DOM to a common parent element, then selecting the target element from that parent.As there are more templates that have same structure with button having class catbtn. Then you can update this part of your code:-