JavaScript ListItem onClick issue - javascript

I'm working on a web-app in HTML and JS and I need to catch the clicks on each listItem of a list.
The list is dynamically created in a for-loop as follows:
var i = document.createElement("li");
i.innerHTML = "something"
uList.appendChild(i);
How can I make listItems clickable or catch clicks on each of them separately (get the specific item clicked)?
I've tried adding an eventListener on the list, and then isolate the target of the event (click). The problem is that this would count as many clicks as the size of the list.

Bind the onclick listener to the new element.
i.onclick = () => {
console.log('hi');
};

Related

Why does defining a variable outside a function produce different behavior than when defined inside a function? Scoping related?

I'm relatively new to JavaScript and in this program I encountered some strange behavior that I would like to understand.
Some context: resetButton is a button to reset the elements in boxes to their initial state. As part of this process, I clone all the elements in boxes to remove their event listeners. Next, I would like to re-add the event listeners to the new elements. This is where the problem arises.
In the first code snippet, boxes is defined outside the initializeBoxes and the addEventListener for resetButton and it seems like I am unable to add event listeners to the elements in boxes
In the second code snippet, boxes is defined inside each function and this code works fine. It looks like the event listeners are cleared and re-added when initalizeBoxes is called.
I think this has to do with scope, however I thought that initalizeBoxes's closure would capture boxes from outside the function. This is where I can't understand what is really happening. Any insights would be greatly appreciated!
let boxes = document.querySelectorAll('.box');
let resetButton = document.getElementById('reset');
resetButton.addEventListener('click', () => {
boxes.forEach((box) => {
box.replaceWith(box.cloneNode(true));
});
initalizeBoxes();
});
function initalizeBoxes() {
boxes.forEach((box) => {
box.addEventListener('click', () => {
// do stuff
let resetButton = document.getElementById('reset');
resetButton.addEventListener('click', () => {
let boxes = document.querySelectorAll('.box');
boxes.forEach((box) => {
box.replaceWith(box.cloneNode(true));
});
initalizeBoxes();
});
function initalizeBoxes() {
let boxes = document.querySelectorAll('.box');
boxes.forEach((box) => {
box.addEventListener('click', () => {
// do stuff
In the first snippet, the boxes variable refers to the collection of elements matching .box that existed at the moment that line was run - let boxes = document.querySelectorAll('.box'); - so, those elements when the page was loaded. The first time the button is clicked, boxes.forEach((box) => { box.replaceWith may properly iterate over all those elements. But then if you click the button again sometime, all elements in the boxes variable refer to elements no longer in the document, so calling .replaceWith on them doesn't affect anything in the current DOM.
In contrast, if you select the boxes inside the button's click listener, you'll be selecting all boxes that exist in the DOM at that point, so you'll be guaranteed to have an up-to-date collection to iterate over and replace.

Dynamic data opens only first element in the row

I have some data fetched and they all have the same buttons for opening modal, but each modal should have content about that specific item that has been clicked.
Button is shown on all items in the row, but when I click it it opens modal only on the first item in the row.
I have simple button in HTML
// this is part of fetched items in a row, each item has this button
<div>
<button id="openUniqueModal">
BUTTON
</button>
</div>
And open function in JS
let openUniqueModalBtn = document.getElementById('openUniqueModal');
if (openUniqueModalBtn != null) {
document.getElementById(
'openUniqueModal'
).onclick = function () {
modal.style.display = 'block';
};
.....
.....
How can I achieve that each button item in row will get triggered onclick? Do I have to loop through it in JS code?
EDIT:
another approach
<button class="openUniqueModal">BUTTON</button>
document
.querySelectorAll('.openUniqueModal')
.addEventListener('click', (event) => {
myModal.style.display = 'block';
});
You don't need to attach event listener to every button instead you can use add a common class to add buttons and then add listener just once
document.querySelector('.btn').addEventListener('click', (event) => {
// you can check button id and take different actions if needed
// event.target.id
});
you can use Zohaib Ijaz's answer and you can even add a wild card if you want to the '.btn' phrase.
[id^='openUniqueModal'] will match all ids starting with openUniqueModal.
[id$='openUniqueModal'] will match all ids ending with openUniqueModal.
[id*='openUniqueModal'] will match all ids containing openUniqueModal.
Please don't use duplicate ids on elements. as button 'openUniqueModal' will be duplicated in your dom for each row as there will be no increment on the id.
Having elements with the same id will cause lots of problems when it comes to industrial.
if you're using jquery you use the below function,
$( "#openUniqueModal" ).on( "click", function() {
// your desired functionality here
// use $( this ) to access current element
});
*I would've added this as a comment in Zohaib Ijaz's answer but I don't have enough permission.

Deleting item from list ( li element), giving index -1. Javascript ( Chrome Extension)

I am making a chrome extension, in which I am adding an item in the list and it has a button to delete the item.
It performs its function as intended but is giving an error in the extension panel.
Uncaught TypeError: Cannot read property 'style' of null
For line : li.style.display = "none";
Upon adding alert in the function, I see that when any of the delete button is clicked first the intended index is alerted and in some cases, -1 is also alerted.
Code which is adding the item and has the delete functionality as well :
function addItem(value){
var li = document.createElement("LI");
var para = document.createElement("P");
var deleteButton = document.createElement("BUTTON"); // Create a <p> node
var t = document.createTextNode(value); // Create a text node
deleteButton.className = "delete";
para.appendChild(t);
li.appendChild(para);
li.appendChild(deleteButton);
textList.appendChild(li);
$(".delete").click(function () {
var index = $(this).index(".delete");
alert(index); //This alert sometimes gives -1
var li = this.parentElement;
li.style.display = "none";
removeItem(index); //This function just removes it from chrome local storage
$(".delete").eq(index).remove();
})
}
Code not posted just initializes list and add item upon button click.
What could be the reason for this -1 index being triggered and how can it be fixed?
When you do this: $(".delete").click(...
...you're adding a click callback to every delete button that exists at the time you execute that code, including the delete buttons that already exist, not just the individual delete button you just created.
After you've added three items, for example, the first item's delete button will have three click callbacks, the second will have two click callbacks, and the third will have one.
When you delete the first item, it will smoothly delete once, then twice give you the -1 error, because it has already deleted itself and won't be found.
I think if you change the code like this:
$(deleteButton).click(...
...that should fix the problem.
The issue is because you're attaching multiple event handlers to the existing .delete buttons every time you add a new row.
A better approach is to use a single delegate event handler for them all. Also note that you can make the code more succinct by using jQuery, as you already have a reference to it in the page. Try this:
function addItem(value) {
$(textList).append(`<li><p>${value}</p><button class="delete"></button></li>`);
}
jQuery($ => {
$(textList).on('click', '.delete', function() {
let $button = $(this);
removeItem($button.index('.delete'));
$button.closest('li').remove();
});
});

How do I get an event listener to work on an element that is added to my html via javascript?

I'm adding some HTML to my document via javascript. I also add a button to my HTML via this javascript function. I want this button to have an event listener, but this doesn't seem to work on the element added with javascript. Is it possible to fix this?
my js:
$('#content-overview li').on('click',function(){
// Clear previous videos
$('.content-overview-row').remove();
// Insert new video
var vimeoId = $(this).data('vimeo');
var html = '<li class="content-overview-row"><div class="content-overview-video"><iframe src="http://player.vimeo.com/video/'+vimeoId+'" width="950" height="534"></iframe><a id="close-video"></a></div></li>';
// Find end of row and insert detailed view
var nextRow = $(this).nextAll('.first-in-row:first');
if(nextRow.is('li')){
nextRow.before(html);
}
else{
//last row
if($(this).hasClass('first-in-row'))
{
//first item clicked in last row
$(this).before(html);
}
else{
$(this).prevAll('.first-in-row:first').before(html);
}
}
return false;
$('#close-video').click(function() {
console.log("works");
});
});
close-video is the close button I am talking about.
You need to bind the click event to an element which exists in the DOM when the page is loaded, and delegate the dynamically added element, like so:
$(document).on('click', '#close-video', function() {
...
});
You should change document for the element closest to your #close-video so that it doesn't have to bubble right up to the document.
Also, you're returning false; before the #close-video click handler, so that code would never be executed anyway. Move it outside of your #content-overview li click handler.

JavaScript Clicked Outside of Element

I know with jQuery it is quite easy to see if a user has clicked outside of a particular element and then react to it, much like how clicking outside of an open drop down menu will close it.
I am trying to achieve something similar with JavaScript alone, no jQuery. Here is my current code:
window.onload = function() {
var nav = document.querySelectorAll('ul li.current_page_item');
var navList = nav[0].parentNode;
//Open the navigation menu list
nav[0].onclick = function() {
navList.className = 'open';
}
}
As of now, the code will apply a class of open to an unordered list if its child element li.current_page_item is clicked on.
I would like to trigger an event (to remove the open class) if the user clicks outside of the li.current_page_item list item.
Could someone show how I could listen for then react to an event that would do something like this with JavaScript alone?
Thank you for your time.
Add click on the document that will close the div, and remember to stopPropagation in the li handler or it will propagate to the document as well.
document.onclick = function(e) {
navList.className = 'close';
}
nav[0].onclick = function(e) {
navList.className = 'open';
e.stopPropagation(); // <----
}

Categories