This question already has answers here:
Adding click event listener to elements with the same class
(5 answers)
Closed 8 months ago.
I am trying to add an event listener but no result came. I know JavaScript has a hoisting feature but I believe I tried all except the correct solution.
const cbox = document.querySelectorAll(".box");
function doit() {
for (let i = 0; i < cbox.length; i++){
cbox[i].classList.add("red");
}
}
cbox.addEventListener("click", doit, false);
Can somebody spot the mistake I make?
There are some dissimilarities between the code and the link you have provided. There is no function doit() in there.
You have attached addEvenListener to the NodeList in cbox.addEventListener("click",....., you have to loop through the list and attach the event to the current element:
Try the following:
const cbox = document.querySelectorAll(".box");
for (let i = 0; i < cbox.length; i++) {
cbox[i].addEventListener("click", function() {
cbox[i].classList.toggle("red");
});
}
*,
html,
body {
padding: 0;
margin: 0;
}
.box {
width: 10rem;
height: 10rem;
background-color: yellowgreen;
float: left;
position: relative;
margin: 0.5rem;
transition: .5s all;
}
h3 {
display: block;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.box:not(:first-child) {
margin-left: 1rem;
}
.red {
background-color: orangered;
}
<div id="box1" class="box box1">
<h3>Box 1</h3>
</div>
<div id="box2" class="box box2">
<h3>Box 2</h3>
</div>
<div id="box3" class="box box3">
<h3>Box 3</h3>
</div>
<div id="box4" class="box box4">
<h3>Box 4</h3>
</div>
You can also use Array.prototype.forEach() with arrow function syntax that will allow you to achieve the same with less code:
let cbox = document.querySelectorAll(".box");
cbox.forEach(box => {
box.addEventListener('click', () => box.classList.toggle("red"));
});
*,
html,
body {
padding: 0;
margin: 0;
}
.box {
width: 10rem;
height: 10rem;
background-color: yellowgreen;
float: left;
position: relative;
margin: 0.5rem;
transition: .5s all;
}
h3 {
display: block;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.box:not(:first-child) {
margin-left: 1rem;
}
.red {
background-color: orangered;
}
<div id="box1" class="box box1">
<h3>Box 1</h3>
</div>
<div id="box2" class="box box2">
<h3>Box 2</h3>
</div>
<div id="box3" class="box box3">
<h3>Box 3</h3>
</div>
<div id="box4" class="box box4">
<h3>Box 4</h3>
</div>
ES6 makes this a bit simpler:
document.querySelectorAll(".box").forEach(box =>
box.addEventListener("click", () => box.classList.toggle("red"))
)
Example implementation:
document.querySelectorAll(".box").forEach(box =>
box.addEventListener("click", () => box.classList.toggle("red"))
)
.box {
width: 5rem;
height: 5rem;
background-color: yellowgreen;
display: inline-block;
}
.box.red {
background-color: firebrick;
}
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
You can use forEach on the class or use Event delegation.
const cboxes = document.querySelectorAll(".box");
function doit() {
... do something ...
}
cboxes.forEach(
function(cbox) {
cbox.addEventListener("click", doit,false);
}
);
Notice that I changed your variable name.
EventDelgation
HTML:
<div id="parent">
<div id="box1" class="box box1">
<h3>Box 1</h3>
</div>
<div id="box2" class="box box2">
<h3>Box 2</h3>
</div>
<div id="box3" class="box box3">
<h3>Box 3</h3>
</div>
<div id="box4" class="box box4">
<h3>Box 4</h3>
</div>
</div>
The JS part:
const parent = document.querySelector("#parent");
parent.addEventListener('click', (e) => {
e.target.classList.add('red');
console.log(e.target);
});
Event delegation is much better and it uses fewer resources, as you only use 1 Event listener and no for loop.
Rather than iterate through multiple elements and attach a lot of separate listeners, I would recommend using event delegation and creating a single delegated listener at the root level, and then looking for matching elements that raised the event.
document.body.addEventListener("click", function(e) {
if (e.target.classList.contains("box")) {
doit();
}
})
Further Reading
window.EventTarget.prototype.addDelegatedListener
Adding click event listener to elements with the same class
addEventListener on NodeList
Related
This question already has answers here:
Adding click event listener to elements with the same class
(5 answers)
Closed 8 months ago.
I am trying to add an event listener but no result came. I know JavaScript has a hoisting feature but I believe I tried all except the correct solution.
const cbox = document.querySelectorAll(".box");
function doit() {
for (let i = 0; i < cbox.length; i++){
cbox[i].classList.add("red");
}
}
cbox.addEventListener("click", doit, false);
Can somebody spot the mistake I make?
There are some dissimilarities between the code and the link you have provided. There is no function doit() in there.
You have attached addEvenListener to the NodeList in cbox.addEventListener("click",....., you have to loop through the list and attach the event to the current element:
Try the following:
const cbox = document.querySelectorAll(".box");
for (let i = 0; i < cbox.length; i++) {
cbox[i].addEventListener("click", function() {
cbox[i].classList.toggle("red");
});
}
*,
html,
body {
padding: 0;
margin: 0;
}
.box {
width: 10rem;
height: 10rem;
background-color: yellowgreen;
float: left;
position: relative;
margin: 0.5rem;
transition: .5s all;
}
h3 {
display: block;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.box:not(:first-child) {
margin-left: 1rem;
}
.red {
background-color: orangered;
}
<div id="box1" class="box box1">
<h3>Box 1</h3>
</div>
<div id="box2" class="box box2">
<h3>Box 2</h3>
</div>
<div id="box3" class="box box3">
<h3>Box 3</h3>
</div>
<div id="box4" class="box box4">
<h3>Box 4</h3>
</div>
You can also use Array.prototype.forEach() with arrow function syntax that will allow you to achieve the same with less code:
let cbox = document.querySelectorAll(".box");
cbox.forEach(box => {
box.addEventListener('click', () => box.classList.toggle("red"));
});
*,
html,
body {
padding: 0;
margin: 0;
}
.box {
width: 10rem;
height: 10rem;
background-color: yellowgreen;
float: left;
position: relative;
margin: 0.5rem;
transition: .5s all;
}
h3 {
display: block;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.box:not(:first-child) {
margin-left: 1rem;
}
.red {
background-color: orangered;
}
<div id="box1" class="box box1">
<h3>Box 1</h3>
</div>
<div id="box2" class="box box2">
<h3>Box 2</h3>
</div>
<div id="box3" class="box box3">
<h3>Box 3</h3>
</div>
<div id="box4" class="box box4">
<h3>Box 4</h3>
</div>
ES6 makes this a bit simpler:
document.querySelectorAll(".box").forEach(box =>
box.addEventListener("click", () => box.classList.toggle("red"))
)
Example implementation:
document.querySelectorAll(".box").forEach(box =>
box.addEventListener("click", () => box.classList.toggle("red"))
)
.box {
width: 5rem;
height: 5rem;
background-color: yellowgreen;
display: inline-block;
}
.box.red {
background-color: firebrick;
}
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
You can use forEach on the class or use Event delegation.
const cboxes = document.querySelectorAll(".box");
function doit() {
... do something ...
}
cboxes.forEach(
function(cbox) {
cbox.addEventListener("click", doit,false);
}
);
Notice that I changed your variable name.
EventDelgation
HTML:
<div id="parent">
<div id="box1" class="box box1">
<h3>Box 1</h3>
</div>
<div id="box2" class="box box2">
<h3>Box 2</h3>
</div>
<div id="box3" class="box box3">
<h3>Box 3</h3>
</div>
<div id="box4" class="box box4">
<h3>Box 4</h3>
</div>
</div>
The JS part:
const parent = document.querySelector("#parent");
parent.addEventListener('click', (e) => {
e.target.classList.add('red');
console.log(e.target);
});
Event delegation is much better and it uses fewer resources, as you only use 1 Event listener and no for loop.
Rather than iterate through multiple elements and attach a lot of separate listeners, I would recommend using event delegation and creating a single delegated listener at the root level, and then looking for matching elements that raised the event.
document.body.addEventListener("click", function(e) {
if (e.target.classList.contains("box")) {
doit();
}
})
Further Reading
window.EventTarget.prototype.addDelegatedListener
Adding click event listener to elements with the same class
addEventListener on NodeList
I got a row, with X amount of columns inside it. How can I, if there is an overflow of columns, replace the regular scrollbar, with left/right image arrows on each side. See the less than and greater than arrows in the below example to see what I mean.
I want to keep it vanilla JS, not jQuery.
div {
display: inline-block;
box-sizing: border-box;
position: relative;
}
.container {
width: 300px;
border: 1px solid black;
position: relative;
white-space: nowrap;
}
.row {
width: 90%;
overflow-x: scroll;
}
.col {
width: 100px;
height: 30px;
}
.grey {
background-color: #cccccc;
}
<div class="container">
<
<div class="row">
<div class="col">
one
</div>
<div class="col grey">
two
</div>
<div class="col">
three
</div>
<div class="col grey">
four
</div>
<div class="col">
five
</div>
<div class="col grey">
six
</div>
</div>
>
</div>
If you don't want to use jQuery, we can add ID's to the lt and gt symbols. This way we can target the images in an onclick event.
HTML:
<div class="container">
<span id="lt"><</span>
<div id="row">
<div class="col">
one
</div>
...
<div class="col grey">
six
</div>
</div>
<span id="gt">></span>
</div>
Then add onclick handlers to the symbols to scroll through the row.
JS:
var row = document.getElementById("row");
row.scrollRight = 0;
row.scrollLeft = 0;
document.getElementById("lt").onclick = function(){
row.scrollLeft = row.scrollLeft - 50;
}
document.getElementById("gt").onclick = function(){
row.scrollLeft = row.scrollLeft + 50;
}
Last, lets remove the horizontal scrollbar.
CSS:
#row {
width: 90%;
overflow-x: hidden;
}
Working fiddle here: https://fiddle.jshell.net/26jrtdky/1/
I have three div elements that has a close button (simplified):
<div class="box1" id="box1">
<span>close</span>
</div>
<div class="box2" id="box2">
<span>close</span>
</div>
<div class="box3" id="box3">
<span>close</span>
</div>
If I click the close button, box 1 will be removed via the
jquery removed() function. Consequently, the class of box 2 will be box1, as well as its ID. The same thing goes for the box 3 whose ID and CLASS will become box2.
I used add and remove class, as well as attr('oldID','newID') functions to
achieve this. The problem is, when I try to access the new box 1 (formerly box 2) and use something like $('#box1').fadeOut(), the one that disappears is box 2
(formerly box 3).
Do you know why this happens?
removed() is not a jQuery function. Instead, the jQuery method .remove() is used to remove the set of matched elements from the DOM.
Please, consider the ID must be unique and there is no need to change them. You may simply work on classes.
Consider to avoid global variables and to use data attribute to preserve ordering.
Your jsfiddle can be reduced to:
$('.header').on('click', function(e) {
//
// decide the animation direction....
//
var newBottom = ($(this).closest('.box').css('bottom') == '-180px') ? '0px' : '-180px';
$(this).closest('.box').animate({'bottom':newBottom}, 200);
});
$('.close').on('click', function(e) {
$(this).closest('.box').remove();
$('.close').each(function(idx, ele) {
var classNames = ['', 'second', 'third'];
//
// toggle classes
//
$(this).closest('.box').toggleClass(classNames[this.dataset.sortOrder - 1] + ' ' + classNames[idx]);
var sortOrder = idx + 1;
this.dataset.sortOrder = sortOrder;
//
// adjust titles
//
$(this).prev('.title').text(function(idx, txt) {
return txt.replace(/\d$/, sortOrder)
})
})
});
.box {
background: #ffffff;
height: 200px;
width: 150px;
bottom: 0;
z-index: 10;
right: 30px;
position: fixed;
border-top-right-radius: 5px;
border-top-left-radius: 5px;
box-shadow: -2px 2px 20px #D8D8D8;
}
.second {
right: 200px;
}
.third {
right: 370px;
}
.header {
height: 30px;
width: 200px;
cursor : pointer;
}
.title {
float:left;
background: #53DD6C;
}
.close {
float:right;
cursor : pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class="box" id="first-box">
<span class="header" id="first-header">
<span class="title">Box 1</span>
<span class="close" data-sort-order="1" id="first-close">X</span>
</span>
</div>
<div class="box second" id="second-box">
<span class="header" id="second-header">
<span class="title">Box 2</span>
<span class="close" data-sort-order="2" id="second-close">X</span>
</span>
</div>
<div class="box third" id="third-box">
<span class="header" id="third-header">
<span class="title">Box 3</span>
<span class="close" data-sort-order="3" id="third-close">X</span>
</span>
</div>
Looking at your jsfiddle, it looks like a lot of overkill. You should be able to manage each element with one common function.
$('.header').on('click', function() {
// Add or remove the 'expanded' class
$(this).toggleClass('expanded');
var boxId = $(this).attr('id').substring('header-'.length);
// Set up the new bottom for animation
var bottomPx = '-180px';
if ($(this).hasClass('expanded')) {
bottomPx = '0px';
}
$('#' + boxId).animate({'bottom':bottomPx}, 200);
});
$('.close').on('click', function() {
var boxId = $(this).attr('id').substring('close-'.length);
$('#' + boxId).remove();
});
.box {
background: #ffffff;
height: 200px;
width: 150px;
bottom: 0;
z-index: 10;
right: 30px;
position: fixed;
border-top-right-radius: 5px;
border-top-left-radius: 5px;
box-shadow: -2px 2px 20px #D8D8D8;
}
.second {
right: 200px;
}
.third {
right: 370px;
}
.header {
height: 30px;
width: 200px;
cursor : pointer;
}
.title {
float:left;
background: #53DD6C;
}
.close {
float:right;
cursor : pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="box" id="box1">
<span class="header expanded" id="header-box1">
<span class="title">Box 1</span>
<span class="close" id="close-box1">X</span>
</span>
</div>
<div class="box second" id="box2">
<span class="header expanded" id="header-box2">
<span class="title">Box 2</span>
<span class="close" id="close-box2">X</span>
</span>
</div>
<div class="box third" id="box3">
<span class="header expanded" id="header-box3">
<span class="title">Box 3</span>
<span class="close" id="close-box3">X</span>
</span>
</div>
i am noob in jquery. I am trying to get this animation:
when the mouse over or click each menu collapse separately and when i click another time, the menu scroll up again. I have write this code but i don't know why it don't work ! Please help.
Thanks
$(document).ready(function() {
$(".lead-title-index").on('click',function() {
if(clicked)
{
clicked=false;
&(".featured-content").slideDown("slow");
}
else
{
clicked=true;
&(".featured-content").slideUp("slow");
}
});
});
.featured-content {
width: 200px;
height: 400px;
display: none;
background: #2E2E2E;
}
.lead-title-index {
background: #FFBF00;
display: block;
margin-bottom: 0px;
position: relative;
color: #000;
font-size: 14px;
width: 200px;
height: 70px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.5/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<div class="col-md-15 col-sm-3">
<div class="lead-title-index">
</div>
<article id="video" class="featured-content">
<div class="promo-text">
</div>
</article>
</div>
<div class="col-md-15 col-sm-3">
<div class="lead-title-index">
</div>
<article id="video" class="featured-content">
<div class="promo-text">
</div>
</article>
</div>
You can try this:
$(document).ready(function() {
$(".lead-title-index").on('click',function() {
$(".featured-content").slideToggle("slow");
});
});
.featured-content {
width: 200px;
height: 400px;
display: none;
background: #2E2E2E;
}
.lead-title-index {
background: #FFBF00;
display: block;
margin-bottom: 0px;
position: relative;
color: #000;
font-size: 14px;
width: 200px;
height: 70px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.5/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div class="col-md-15 col-sm-3">
<div class="lead-title-index">
</div>
<article id="video" class="featured-content">
<div class="promo-text">
</div>
</article>
</div>
<div class="col-md-15 col-sm-3">
<div class="lead-title-index">
</div>
<article id="video" class="featured-content">
<div class="promo-text">
</div>
</article>
</div>
There are several mistakes here.
First:
Change
&(".featured-content").slideDown("slow");
&(".featured-content").slideUp("slow");
to
$(".featured-content").slideDown("slow");
$(".featured-content").slideUp("slow");
As & is not a valid jquery syntax.
Second:
the variable clicked is not defined. You should define it globally after document.ready function.
Your final code should look something like this:
$(document).ready(function () {
var clicked;
$(".lead-title-index").on('click', function () {
if (clicked) {
clicked = false;
$(".featured-content").slideDown("slow");
} else {
clicked = true;
$(".featured-content").slideUp("slow");
}
});
});
You can check out the working demo here.
First off, you don't need the clicked flag. Change & to $ (I am sure it's a mistake). All you need is as simple as the following.
$(document).ready(function() {
$(".lead-title-index").on('click',function() {
var $featuredContent = $(".featured-content");
$featuredContent.is(":hidden") && $featuredContent.slideDown("slow") || $featuredContent.slideUp("slow");
});
});
.featured-content {
width: 200px;
height: 400px;
display: none;
background: #2E2E2E;
}
.lead-title-index {
cursor: pointer;
background: #FFBF00;
display: block;
margin-bottom: 0px;
position: relative;
color: #000;
font-size: 14px;
width: 200px;
height: 70px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.5/d3.min.js"></script>
<div class="col-md-15 col-sm-3">
<div class="lead-title-index">
</div>
<article id="video" class="featured-content">
<div class="promo-text">
</div>
</article>
</div>
<div class="col-md-15 col-sm-3">
<div class="lead-title-index">
</div>
<article id="video" class="featured-content">
<div class="promo-text">
</div>
</article>
</div>
Even better, try jQuery.slideToggle which is a short hand for slideUp and slideDown based on the display state of the element.
$(document).ready(function() {
$(".lead-title-index").on('click',function() {
$(".featured-content").slideToggle("slow");
});
});
thanks for all of you folk, but what i need is that:
in first time, when mouse hover .lead-title-index , the .featured-content will appear.
In second time, when the user click .lead-title-index , the .featured-content will disappear.
So I have 4 divs. I want to change the size of the inner divs compared to parent divs.
I want to dynamically change the child div size related to parent's one.
Now I've added .top class, but I don't really know if its needed or if it will be useful.
Here is the fiddle I'm testing with
http://jsfiddle.net/y3597/171/
jQuery below
$(".top").each(function () {
$('.object').width($(".inner").parent().width());
});
CSS below:
.container1 { width: 200px; background: red; padding: 2px; }
.container2 { width: 225px; background: purple; padding: 2px; }
.container3 { width: 250px; background: blue; padding: 2px; }
.container4 { width: 275px; background: black; padding: 2px; }
/* top ? */
.inner { width: 150px; background: gray; }
.object { width: 100px; background: green; }
HTML below:
<div class="container1 top">
<div class="inner">
<div class="object">Text 1</div>
</div>
<div class="container2 top">
<div class="inner">
<div class="object">Text 2</div>
</div>
<div class="container3 top">
<div class="inner">
<div class="object">Text 3</div>
</div>
<div class="container4 top">
<div class="inner">
<div class="object">Text 4</div>
</div>
I think that you are trying to achieve this:
$(".top").each(function () {
$(this).find(".object").width($(this).width());
});
In your code jQuery will check for every element with .object class in DOM on each loop. When you use (this) you are refering to element that is currently "selected" in loop.
Better way to achive this is to set widths od children to 100%, so they will inherit the witdhs from parents.