Working with event listeners
For the past week I've been working on my final school project and something that had been a bit of a challenge for me ended up being event listeners. Over the course of a few assignments that I had been doing, I simply did not understand why mine never worked as intended and as I was doing my final project I finally got the cause of my problem with them and I'll try as much to explain (I'm still a beginner there may be a few problems or mistakes so please bear with me) how I got to get over the issues I was facing.
Correct positioning of event listeners
In my final project, we needed to work with API's for our websites and I chose the NASA Astronomical Picture of the Day API which basically shows a really captivating astronomical picture every day.
I managed to create a simple web page which showed a particular picture with it's description and it had some functionalities such as searching a particular date, moving to the next or previous date and a history section which recorded the dates you visited. I wanted the history section to be such that you could click on a particular date and the picture with the explanation would show up. I created an array for the date history and ensured visiting a particular date twice wouldn't record it twice.
//my initial attempt
const dateHistoryArray = []
function renderData(data) {
// Code for rendering data...
// Extract the new date from the fetched data
let newDate = data.date;
// Check if the new date is not already in the history array
if (dateHistoryArray.indexOf(newDate) === -1) {
// If it's a new date, add it to the history array
dateHistoryArray.push(newDate);
// Create a new container element to hold the date and delete button
const container = document.createElement("div");
container.innerHTML = `<span style="display:inline-block"><li class="listItem">${newDate}</li></span> <span style = "display:inline-block"><button class = "listDelButton">x</button></span>`;
dateHistoryList.appendChild(container);
}
//attempt to try and render the dates
const dateHistoryList = document.querySelectorAll(".listItem")
dateHistoryList.forEach((item) => {
item.addEventListener("click", ...
}
Initially, my intention was to immediately attach event listeners to the dynamically rendered dates to handle user interaction, such as clicking on a date to display its corresponding image.
However, the asynchronous nature of fetching data meant that the querySelectorAll
method initially returned an empty list, as the dates had not yet been appended to the DOM meaning the event listeners wouldn't work anyway.
I then had the idea to simply encapsulate the event listener within a function then call it each time a new date has been appended. While it initially appeared to work, I encountered a problem. Clicking on a date would sometimes display the wrong image or cycle through multiple images. At the time I simply couldn't understand why that was happening.
function renderData(data) {
// Code for rendering data...
// Extract the new date from the fetched data
let newDate = data.date;
// Check if the new date is not already in the history array
if (dateHistoryArray.indexOf(newDate) === -1) {
// If it's a new date, add it to the history array
dateHistoryArray.push(newDate);
// Create a new container element to hold the date and delete button
const container = document.createElement("div");
container.innerHTML = `<span style="display:inline-block"><li class="listItem">${newDate}</li></span> <span style = "display:inline-block"><button class = "listDelButton">x</button></span>`;
dateHistoryList.appendChild(container);
const dateElement = container.querySelectorAll(".listItem")
eventListenerFunction(dataElement)
}
function eventListenerFunction(dataElement) {
dataElement.forEach((date) => {
dataElement.addEventListener("click", ...)
}
}
Issues with multiple event listeners
I did some research and realized i had been doing it all wrong all this time. After some research, I discovered the root cause: multiple event listeners were being added to previously recorded dates with each new addition. This resulted in chaotic behavior and inconsistent rendering of images.
It took a while to find the solution. To attach the event listener immediately after adding a new date, preventing the accumulation of redundant listeners, then do we pass on the function that actually renders the date. Though seemingly straightforward, this realization marked a significant breakthrough in my understanding of event handling in JavaScript.
let newDate = data.date;
if (dateHistoryArray.indexOf(newDate) === -1) {
dateHistoryArray.push(newDate);
const container = document.createElement("div");
container.innerHTML = `<span style="display:inline-block"><li class="listItem">${newDate}</li></span> <span style = "display:inline-block"><button class = "listDelButton">x</button></span>`;
dateHistoryList.appendChild(container);
let listItem = container.querySelector(".listItem");
listItem.addEventListener("click", renderListItem);
const delButton = container.querySelector(".listDelButton");
delButton.addEventListener("click", removeDate);
This is still an earlier project with many to come. It's good knowing there's still a lot more to learn and that I can actually overcome the blocks I face. It's not the end of the road. I'll keep trying till I make it.