.addEventListener - long list - javascript

I found few answer on my issue but probably I'm not so experienced to processes it to my case.
I have list of items generated to .html
<div id="grid">
by JavaScript
var div_block = "";
for (i = 0; i < 70; i++) {
div_block = div_block + '<div id="c' + i + '" class="card"></div>';
}
document.getElementById("grid").innerHTML = div_block;
for (i = 0; i < 70; i++) {
'var c' + i + ' = document.getElementById(c' + i + ');'
}
and it works fine.
I want to chose .addEventListner method to chose proper element but for 70 and more elements code:
c0.addEventListener("click", function () {revealCard(0);});
c1.addEventListener("click", function () {revealCard(1);});
...
cn.addEventListener("click", function () {revealCard(n);});
is huge and not elegant. Method I've tried didn't work
for (i = 0; i < 70; i++) {
'c'+i+'.addEventListener("click", function() { revealCard('+i+'); });'
}
How to build working addEventListener() for this?
Thanks a lot.

The problem you are facing can be solved by using the card class that you add on each of your card. Then, to refer to the right card, you can use the keyword this, which in the context of an addEventListener will refer to whichever DOM element received the click. You also won't need to generate a unique Id for each one of your div, which I think is a big plus.
Your code would look like this:
let div_block = "";
for (i = 0; i < 70; i++) {
div_block = div_block + '<div class="card"></div>';
}
const cards = querySelectorAll(".card");
cards.forEach(card => {
card.addEventListener("click", revealCard)
})
function revealCard(){
// Here, `this` refers to the card that was clicked
// So you can do what you want with it
console.log(this);
}

Slight modification to brk's answer, using a single event listener on the parent that will trigger for the events on the children
var div_block = "";
for (let i = 0; i < 5; i++) {
div_block += `<div data-attr="${i}" id="c${i}" class="card">Hello</div>`;
}
var grid = document.getElementById("grid");
grid.innerHTML = div_block;
grid.addEventListener('click', function (e) {
if (e.target.getAttribute('class') === 'card') {
revealCard(e.target.dataset.attr);
}
});
function revealCard(num) {
console.log(num)
}
<div id='grid'></div>

You can use dataset, that is while creating the dom add a dataset property.
Then use querySelectorAll to get all the div with class card and iterate over it to add event using addEventListener. On click of the element get the dataset value and pass to revealCard function
var div_block = "";
for (let i = 0; i < 5; i++) {
div_block += `<div data-attr="${i}" id="c${i}" class="card">Hello</div>`;
}
document.getElementById("grid").innerHTML = div_block;
document.querySelectorAll('.card').forEach(function(item) {
item.addEventListener('click', function(e) {
revealCard(item.dataset.attr)
})
})
function revealCard(num) {
console.log(num)
}
<div id='grid'></div>

There are multiple ways to do this, but I wouldn't use IDs and I wouldn't bind X event listeners. I would use event delegation and data-* attributes:
Build your list of elements:
const grid = document.getElementById("grid");
for (var i = 0; i < 70; i++) {
const child = document.createElement('div');
child.className = 'card';
child.dataset.index = i;
grid.appendChild(child);
}
Add an event listener to the grid element:
grid.addEventListener('click', function(event) {
let target = event.target;
// traverse up if clicked inside element
while (target.className !== 'card') {
target = target.parentNode;
}
if (target) {
revealCard(target.dataset.index);
}
});
const grid = document.getElementById("grid");
for (var i = 0; i < 70; i++) {
const child = document.createElement('div');
child.className = 'card';
child.dataset.index = i;
child.innerText = `Card ${i}`;
grid.appendChild(child);
}
grid.addEventListener('click', function(event) {
let target = event.target;
// traverse up if clicked inside element
while (target.className !== 'card') {
target = target.parentNode;
}
if (target) {
revealCard(target.dataset.index);
}
});
function revealCard(i) {
console.log(`Card ${i} revealed`);
}
<div id="grid"></div>

I found probably the easiest (shortest) solution:
for(var i=0;i<boardSize;i++){
document.getElementById('c'+i).addEventListener("click",function(){
revealCard(this.id.substring(1));
});
}
What do you thing?

Related

Using an EventListener on the Element that calls a counter function - Javascript

Goal: Write a function that is called by the eventListener click event of an IMG. The function will use a conditional to see on which ElementID called the function, then initialize the on accumulator to that value, then add 1 and change the innerHTML to show that new value. Key goal is to make this occur with out have 3 similar identical functions
Code Thus Far
let likeCount = !NaN;
var classname = document.getElementsByClassName("like-heart");
function likeCounter() {
/*Note the elementID is very similar to the ELementID of the accumulator element - but not the same.*/
if (this.getElementById === "ft-recipe-like") {
likeCount = Number(document.getElementById('featured-likes').innerText);
likeCount += 1;
document.getElementById('featured-likes').innerText = likeCount;
} else if (this.getElementById === "macaroon-like") {
likeCount = Number(document.getElementById('macaroon-likes').innerText);
likeCount += 1;
document.getElementById('macaroon-likes').innerText = likeCount;
} else if (this.getElementById === 'brulee-like') {
likeCount = Number(document.getElementById('brulee-likes').innerText);
likeCount += 1;
document.getElementById('brulee-likes').innerText = likeCount;
}
for (var i = 0; i < classname.length; i++) {
classname[i].addEventListener('click', likeCounter, false);
}
}
}
}
for (var i = 0; i < classname.length; i++) {
classname[i].addEventListener('click', likeCounter, false);
}
}
}
You can have a map of acc id to Dom node id, and write a generic function to increment the likeCount.
let likeCount = !NaN;
var classname = document.getElementsByClassName("like-heart");
const accIdToDomIdMap = {
'ft-recipe-like': 'featured-likes',
'macaroon-like': 'macaroon-likes',
'brulee-likes': 'brulee-likes'
}
function likeCounter() {
/*Note the elementID is very similar to the ELementID of the accumulator element - but not the same.*/
if(accIdToDomIdMap.hasOwnProperty(this. getElementById)) {
const domId = document.getElementById(accIdToDomIdMap[this.getElementById]);
likeCount = (+domId.innerText || 0) + 1;
domId.innerText = likeCount;
}
}
function newFunction() {
for (var i = 0; i < classname.length; i++) {
classname[i].addEventListener('click', likeCounter, false);
}
}
}

How do i convert a Jquery code to Pure javascript

I am using Bootstrap.
I am not able to figure out how to put this in pure javascript.This will open a div when we click on the accordion.
$(function() {
$("#panelTicketsList .list-group-item").on("click", function() {
$("#panelTicketsList .list-group-item").removeClass('selected');
$(this).addClass('selected');
if ($('#panelTicketsList').hasClass('col-md-12')) {
$('#panelTicketsList').removeClass('col-md-12').addClass('col-md-3');
$('.panelTicketDetail').removeClass('hide');
}
});
});
jsFiddle : https://jsfiddle.net/tqdc6yyL/
var listGroupItems = document.getElementsByClassName('list-group-item');
for (j = 0; j < listGroupItems.length; j++) {
listGroupItems[j].addEventListener("click", function () {
var elements = listGroupItems;
for (i = 0; i < elements.length; i++) {
if (elements[i].className.indexOf("col-md-12") > -1) {
elements[i].className = elements[i].className.replace("col-md-12", "col-md-3");
elements[i].className = elements[i].className.replace("hide", "");
}
}
this.className = this.className + " selected";
});
}
var list = document.getElementById('panelTicketsList');
var items = document.querySelectorAll("#panelTicketsList .list-group-item");
var detail = document.querySelectorAll(".panelTicketDetail");
items.forEach(function(btn){
btn.addEventListener("click", function(){
items.forEach(function(item){ item.classList.remove("selected"); });
this.classList.add("selected");
if(list.classList.contains('col-md-12')) {
list.classList.remove('col-md-12');
list.classList.add('col-md-3');
detail.classList.add("hide");
}
});
If you have to support older browsers like IE8 or IE9, you can't use JS features like forEach or classList. Instead you should use for loop and className.
//Save DOM query in variable for reuse
var panelTicketsList = document.getElementById('panelTicketsList');
var panelTicketsDetails = document.getElementsByClassName('panelTicketDetail');
var listGroupItems = panelTicketsList.getElementsByClassName('list-group-item');
//go through all of the listGroupItems and set click listener
for (var i = 0; i < listGroupItems.length - 1; i++) {
listGroupItems[i].addEventListener("click", function() {
//On click, go through all of listGroupItems and remove selected class
for (var j = 0; j < listGroupItems.length - 1; j++) {
listGroupItems[j].className = listGroupItems[j].className.replace('selected', '');
}
//Add selected class for clicked element
listGroupItems[i].className += 'selected';
//test if main element has class col-md-12
if (panelTicketsList.className.indexOf("col-md-12") > -1) {
//replace clas col-md-12 with col-md-3
panelTicketsList.className = panelTicketsList.className.replace('col-md-12', 'col-md-3');
//go through all of the panelTicketDetails and remove hide class
for (var k = 0; k < panelTicketsDetails.length - 1; k++) {
panelTicketsDetails[k].className = panelTicketsDetails[k].className.replace('hide', '');
}
}
});
}

Passing an argument to a event listener from dynamically created buttons

I'm dynamically creating 3 buttons. How can I pass an argument tohandlerX?
So basically I want the values in the category Array to be passed on to the handlerX eventListener.
Example:
When myBtn1 is clicked, I want the alert to be "fur_",
When myBtn3 is clicked, I want the alert to be "fas_"
var btns = '';
var category = ["fur_", "fts_", "fas_"];
for (i = 1; i < category.length; i++) {
btns += '<button type="button" class=' + category[i] + ' id= "myBtn' + i + '">.....</button>';
}
var div = document.getElementById('div');
div.innerHTML = btns;
var handlerX = function () {
alert('Clicked'); //get value from the 'category' Array
};
var buttons = div.querySelectorAll('button');
for (var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', handlerX, false);
}
The answers given so far are good and should solve your problem. Just thought I'd add this one because I think it's a solution more in line with what you were asking for: Make your handlerX return a function like so:
var handlerX = function (param) {
return function() {alert(param);};
};
var buttons = div.querySelectorAll('button');
for (var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', handlerX(category[i]), false);
}
Edit: Here's a Fiddle
If you're willing to extract it from the class attribute, then:
var handlerX = function () {
alert(this.getAttribute('class'));
};
Or you better associate it with some data- attribute. For example:
for (i = 1; i < category.length; i++) {
btns += '<button type="button" data-category="' + category[i] + '" class=' + category[i] + ' id= "myBtn' + i + '">.....</button>';
}
Then:
var handlerX = function () {
alert(this.getAttribute('data-category'));
};
See Fiddle
EDIT:
then i would reccomend adding an attibute: data-category="fur_" for example, and access that from your event handler:
this.getAttribute('data-category')
in hadlerX there is a "this" that is the element that was clicked. You can access its getAttribute("class") to get the class EDIT:this, not self
var fragment = document.createDocumentFragment(),
categories = ["fur_", "fts_", "fas_"],
btn;
function onClickBtn() {
alert(this.getAttribute("data-category"));
}
for (var i = 0; i < categories.length; i++) {
btn = document.createElement("button");
btn.id = "myBtn" + string(i);
btn.setAttribute("data-category", category[i]);
btn.addEventListener("click", onClickBtn);
fragment.appendChild(btn);
}
var div = document.getElementById('div');
div.appendChild(fragment);

How do I change an html cell's color on click, if the table is generated dynamically?

function drawTable() {
var table = document.getElementById("myTable");
var input = prompt("Insert height (in number of cells)");
var a = +input;
var input2 = prompt("Insert width (in number of cells)");
var b = +input2;
for (var i = 0; i < a; i++) {
var row = table.insertRow(i);
for (var j = 0; j < b; j++) {
var cell = row.insertCell(j);
};
};
};
I can't for the life of me figure out how to add a onClick event that would change the color of a cell. Do I create a new function in JavaScript and add an onClick event to the table element? That's what I did, but it doesn't seem to work.
function changeColor() {
var td = document.getElementsById("myTable").getElementsByTagName("td");
td.style.backgroundColor = "red";
}
for (var j = 0; j < b; j++) {
var cell = row.insertCell(j);
cell.addEventListener("click", changeColor.bind(cell), false);
};
function changeColor(e) {
this.style.backgroundColor = "red";
}
Should do the trick. Every cell gets an onclick handler set in the for loop. Bind passes the reference of the cell to the changeColor function. The function can address the cell by using this.
For some situations, the answer suggested by Mouser work well. But if consider a situation taking your example of table creation based on number of rows and columns, then adding eventlistener to each cell doesn't sound a good approach. Suppose at initial user requested for 10X10 table. At that moment,
eventlistener is added to each cell.
But what if at some later point of time, more rows/columns are added dynamically. In that situation, only thing you will left with is to add event listeners.
Better approach is to understand the term
Event Delegation
In this approach, you add event listener to parent and just listen to event bubbled up(default behavior) by the child elements.In that case you dont have to be worry about dynamically created cells and adding event listeners to those.
You can take a look on working sample with Event Delegation approach on your code at below link:
http://jsfiddle.net/zL690Ljb/1/
function drawTable() {
var table = document.getElementById("myTable");
table.addEventListener("click", changeColor);
var input = prompt("Insert height (in number of cells)");
var a = +input;
var input2 = prompt("Insert width (in number of cells)");
var b = +input2;
for (var i = 0; i < a; i++) {
var row = table.insertRow(i);
for (var j = 0; j < b; j++) {
var cell = row.insertCell(j);
//cell.addEventListener("click", changeColor.bind(cell), false);
};
};
};
function changeColor(event) {
if (event.target && event.target.tagName && (!event.target.tagName.match(/table|th|tr|tbody|thead/i)) )
{
var element = event.target;
while (element && element.parentElement)
{
if(element.tagName=='TD'){
element.style.backgroundColor = "red";
break;
}
else
{
element = element.parentElement;
}
}
}
}
drawTable();
I hope Mouser will agree on this. Thanks!
Inside the for you can add the onclick event for each cell:
for (var i = 0; i < a; i++) {
var row = table.insertRow(i);
for (var j = 0; j < b; j++) {
var cell = row.insertCell(j);
cell.onclick = function(){
changeColor(this);
}
};
};
Then the changeColor will be as following:
function changeColor(cell) {
cell.style.backgroundColor = "red";
}

Adding events to multiple childNodes in a for() respecting index

I need to implement a simple event and dom manipulation, but I can't use jQuery
So I am attaching the event to each single node using for()
document.addEventListener("DOMContentLoaded", function(event) {
var mainList = document.getElementById('mainList');
var mainNodes = document.querySelectorAll('#mainList > li');
for (var i = 0; i < mainNodes.length; i++) {
var node = mainNodes[i];
node.addEventListener('contextmenu', function(e){
e.preventDefault();
var currentActive = document.querySelectorAll('.active');
if(currentActive[0]) { // first item with class 'active'
currentActive[0].className = '';
}
this.className = 'active';
});
var options = node.getElementsByTagName('li');
for (var j = 0; j < options.length; j++) {
var option = options[j];
option.addEventListener('click', function(e){
e.preventDefault();
switch(this.className){
case 'eliminar':
delete(mainList,node);
break;
case 'obrir':
open(mainList,node);
break;
case 'clonar':
clone(mainList,node);
break;
}
node.className = '';
});
}
}
});
The problem here is that even that all elemnts have the event, its allways opened/cloned/deleted the last node,
Doesn't using var node applies only to the current node in the loop?
-fiddle-
http://jsfiddle.net/toniweb/Wx8Jf/34/
Your event handler will be triggered later. When the loop finishes, the node is the last node. You need to create a closure to capture the current node. Something like this:
for (var i = 0; i < mainNodes.length; i++) { (function(node){
//your code
}(mainNodes[i]))
}
Try:
for (var i = 0; i < mainNodes.length; i++) { (function(node){
//Your code
node.addEventListener('contextmenu', function(e){
e.preventDefault();
var currentActive = document.querySelectorAll('.active');
if(currentActive[0]) { // first item with class 'active'
currentActive[0].className = '';
}
this.className = 'active';
});
var options = node.getElementsByTagName('li');
for (var j = 0; j < options.length; j++) {
var option = options[j];
option.addEventListener('click', function(e){
e.preventDefault();
switch(this.className){
case 'eliminar':
delete(mainList,node);
break;
case 'obrir':
open(mainList,node);
break;
case 'clonar':
clone(mainList,node);
break;
}
node.className = '';
});
}
}(mainNodes[i]))
}

Categories