I'm writing a dynamic list and for some reason all my events are triggered using the same ID (all rows of the list can trigger the event, but using the same ID). My rows have different IDs but for some reason, the event is triggered with the latest entry on the list.
Here is the code of the list:
<div class="listContainer" style="width: 100%; overflow-y: auto;">
<div id="card-list" class="container-fluid">
<div class="row row-space" id="row1">
<div class="row-basic" id="rowClass1" style="background-color: rgb(247, 245, 248);">
<div class="circle"></div>
<div class="testTitleContainerBar" style="color: rgb(79, 101, 114);">#1</div>
<div class="totalNumberContainerBar" style="color: rgb(79, 101, 114);">5 Images</div>
<div class="square"><img src="assets/Menu 2-24.png"></div>
<div class="onePixelSeparatorBar"></div>
</div>
<div class="row-advanced" style="height: 0px; visibility: visible;"> </div>
</div>
<div class="row row-space" id="row2">
<div class="row-basic" id="rowClass2">
<div class="circle"></div>
<div class="testTitleContainerBar">#2</div>
<div class="totalNumberContainerBar">5 Images</div>
<div class="square"><img src="assets/Menu 2-24.png"></div>
<div class="onePixelSeparatorBar"></div>
</div>
<div class="row-advanced" style="height: 0px; visibility: visible;"> </div>
</div>
</div>
The event listner is added dynamically like that:
row = document.createElement('div');
row.classList.add('row');
row.classList.add('row-space');
idNumber = total_Tests;
row.id = "row"+idNumber;
row.addEventListener('click', function() {openRow('#'+row.id)});
Can someone help me to understand what's going on?
You are reusing variable row inside a loop, and by the time click handler executes it references the last row. Capture the current id value:
row.addEventListener('click', (function(id) {
return function() { openRow('#'+id); }
})(row.id));
or
row.addEventListener('click', function() {openRow('#'+this.id)});
Related
With the press of a button, I want to toggle the class .active on the next div.bottom. These are basically accordions, but with a different structure.
Using nextElementSibling I guess won't work here to select the target element. How would one select such an element, that's neither a child nor a sibling (in plain JS)?
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button></button></div>
</div>
</div>
<div class="bottom"></div>
</div>
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button></button></div>
</div>
</div>
<div class="bottom"></div>
</div>
I'd do it by using closest to go up to the container .wrapper element, then querySelector to find the bottom element:
function onClick(event) {
const wrapper = event.target.closest(".wrapper");
const bottom = wrapper && wrapper.querySelector(".bottom");
if (bottom) {
bottom.classList.toggle("active");
}
}
Live Example:
// I've added event delegation here
document.body.addEventListener("click", function onClick(event) {
const button = event.target.closest(".inner button");
const wrapper = button && button.closest(".wrapper");
const bottom = wrapper && wrapper.querySelector(".bottom");
if (bottom) {
bottom.classList.toggle("active");
}
});
.active {
color: blue;
border: 1px solid black;
}
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button>Button A</button></div>
</div>
</div>
<div class="bottom">Bottom A</div>
</div>
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button>Button B</button></div>
</div>
</div>
<div class="bottom">Bottom B</div>
</div>
Or the same thing using optional chaining (relatively new):
function onClick(event) {
const wrapper = event.target.closest(".wrapper");
const bottom = wrapper?.querySelector(".bottom");
bottom?.classList.toggle("active");
}
Live Example:
// I've added event delegation here
document.body.addEventListener("click", function onClick(event) {
const button = event.target.closest(".inner button");
const wrapper = button?.closest(".wrapper");
const bottom = wrapper?.querySelector(".bottom");
bottom?.classList.toggle("active");
});
.active {
color: blue;
border: 1px solid black;
}
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button>Button A</button></div>
</div>
</div>
<div class="bottom">Bottom A</div>
</div>
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button>Button B</button></div>
</div>
</div>
<div class="bottom">Bottom B</div>
</div>
By using closest() you can traverse the DOM upwards. With this it's easy to just get the relevant .bottom and toggle the active class on this element.
document.querySelectorAll('button').forEach(button => {
button.addEventListener('click', (e) => {
e.currentTarget.closest('.wrapper').querySelector('.bottom').classList.toggle('active');
});
});
.bottom {
display: none
}
.bottom.active {
display: block
}
<div class="wrapper">
<div class="top">
<div class="inner">
<button type="button">Toggle</button>
</div>
</div>
<div class="bottom">Hidden content</div>
</div>
<div class="wrapper">
<div class="top">
<div class="inner">
<button type="button">Toggle 2</button>
</div>
</div>
<div class="bottom">Hidden content 2</div>
</div>
How do I make:
document.getElementsByClassName("first")[1].getElementsByClassName("second")[2];
but with querySelector?
My guess would be:
document.querySelector(".first[1] > .second[2]");
But that doesn't work.
In your original selection you're grabbing the second element with the class of .first and the third element with the class of .second that is also the child of the former. With this in mind you could use the nth-of-type pseudo selector for both classes and count up accordingly. The only difference with this method in comparison to the JS you have now is that it doesn't use the zero-index.
// document.getElementsByClassName("first")[1].getElementsByClassName("second")[2];
document.querySelector('.first:nth-of-type(2) .second:nth-of-type(3)').style = 'border: 1px solid red;'
.first {
border: 1px solid black;
padding: 10px;
}
.first:nth-of-type(2) {
margin-top: 10px;
}
<div class="first">
<div class="second">Second (1)</div>
<div class="second">Second (2)</div>
<div class="second">Second (3)</div>
</div>
<div class="first">
<div class="second">Second (1)</div>
<div class="second">Second (2)</div>
<div class="second">Second (3)</div>
</div>
document.querySelector(".first:nth-of-type(2) .second:nth-of-type(3)").style.color = "red"
<div class="first">
<div class="second">second1</div>
<div class="second">second2</div>
<div class="second">second3</div>
<div class="second">second4</div>
</div>
<div class="first">
<div class="second">second1</div>
<div class="second">second2</div>
<div class="second">second3</div>
<div class="second">second4</div>
</div>
You don't need the > operator of the querySelector, you could use the following syntax:
document.querySelector('.first:nth-child(1) .second:nth-child(2)');
Within this HTML code:
var test = document.querySelector('.first:nth-child(1) .second:nth-child(2)').innerText;
console.log(test);
<div class="first">
<div class="second"></div>
<div class="second">Hello, I'm your selected div!</div>
<div class="second"></div>
</div>
<div class="first">
</div>
The JS code will produce the output:
Hello, I'm your selected div!
Keep in mind that CSS pseudoselectors start counting from 1, not from 0, so to achieve the example you posted, you'd need to set :nth-child(2) and :nth-child(3).
Also, if you have a different structure, it might as well be worth taking a look at the :nth-of-type selector, as the :nth-child will require to be the nth child of a parent, in an absolute sense. Differently, :nth-of-type will look for the nth (typeof) child of a parent.
For my current project I want to make a sort of fixed element in my DOM.
I am not using position: fixed because the element will lose its existence within the DOM and thus its original position (which in my opinion only makes things look worse). I made the element behave like a fixed element by just adding/removing margin-top: somevalue to the scrollable element, everytime when the user scrolls and the code I use was made possible within the JavaScript. Using this method also adds a nice looking animation to this whole "interaction".
The problem I am experiencing is that when the (browser) window has such a small height, that the element will reach for the footer, it will expand the container, body or whatever parent is on it. How do I prevent this from happening?
I made a JSFiddle per example of this issue.
$(document).ready(function() {
var topPadding = 10;
//Set the scrollable offset before starting the scroll
var scrollableTopOffset = $(".scrollable").offset().top;
$(window).scroll(function() {
/* When scrolling, determine wether the browser window still contains
scrollable: */
if (scrollableTopOffset < $(window).scrollTop()) {
//Code when scrollable is within the browser window...
//$(".shopping-cart").addClass("scrollable-fixed");
$(".scrollable").stop().animate({
marginTop: $(window).scrollTop() - scrollableTopOffset + topPadding
});
} else {
//Code when scrollable is not within the browser window...
//$(".shopping-cart").removeClass("scrollable-fixed");
$(".scrollable").stop().animate({
marginTop: 0
});
}
});
});
.some-content-block {
height: 150px;
margin-bottom: 5px;
background-color: red;
}
.scrollable {
height: 75px;
background-color: cyan;
}
footer {
height: 200px;
background-color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
<div class="container" style="background-color: blue;">
<div class="row">
<div class="col-md-12">
<div class="some-content-block">
</div>
</div>
</div>
<div class="row">
<div class="col-xs-10 col-sm-10 col-md-10">
<div class="col-md-4">
<div class="some-content-block">
</div>
</div>
<div class="col-md-4">
<div class="some-content-block">
</div>
</div>
<div class="col-md-4">
<div class="some-content-block">
</div>
</div>
<div class="col-md-4">
<div class="some-content-block">
</div>
</div>
<div class="col-md-4">
<div class="some-content-block">
</div>
</div>
<div class="col-md-4">
<div class="some-content-block">
</div>
</div>
<div class="col-md-4">
<div class="some-content-block">
</div>
</div>
</div>
<div class="col-xs-2 col-sm-2 col-md-2">
<div class="scrollable">
</div>
</div>
</div>
</div>
<footer></footer>
Edit: Here is my fiddle updated with the answer of SamGhatak: JSFiddle
I think I found a solution here:
https://jsfiddle.net/rv4mg8uq/2/
Added this code there:
if(($('footer').offset().top -scrollableTopOffset +topPadding)<$(window).scrollTop()){
//do nothing
}
I am trying to create a comparison overlay that shows items selected by users when they 'add to compare' link.(like one in flipkart that appears on top when you hit add to compare). Here is my code:
<div class="college-list-container">
<div class = "individual-college-container" id="text1">
<div class="image-header"><h3>text1</h3>
</div>
<div class="dark-overlay">
<div class="overlay-links" style=" float:left;"> <div class="absolute-center ">Details</div></div>
<a href=""> <div class="overlay-links" style=" float:right; border-right:none;"> <div class="absolute-center comparison" id="comparison">Add to compare</div>
</div></a>
</div>
</div>
<div class = "individual-college-container">
<div class="image-header"><h3>text2</h3>
</div>
<div class="dark-overlay">
<div class="overlay-links" style=" float:left;"> <div class="absolute-center ">Details</div></div>
<a href=""> <div class="overlay-links" style=" float:right; border-right:none;"> <div class="absolute-center comparison">Add to compare</div>
</div></a>
</div>
</div>
<div class = "individual-college-container" id="itm">
<div class="image-header" ><h3>text3</h3>
</div>
<div class="dark-overlay">
<div class="overlay-links" style=" float:left;"> <div class="absolute-center ">Details</div></div>
<a href=""> <div class="overlay-links" style=" float:right; border-right:none;"> <div class="absolute-center comparison">Add to compare</div>
</div></a>
</div>
Javascript
/show overlay when one checkbox is checked and add its name/image to the empty space
$('.comparison').click(function(e){
var clgId = $(this).parentsUntil('.individual-clg-container').find('.image-header').text();
e.preventDefault();
var clg = $("<li></li>")
clg.text(clgId);
var removeLink = $("<a href=''>(Remove)</a>");
clg.append(removeLink)
$('.comparison-colleges').append(clg);
$('.add-to-compare-overlay').show("slide", { direction: "up" }, 300);
});
I want the text in the div containing class 'image-header' to be assigned to the variable clgId. The problem that i am facing with my code is that it is adding the text of all the divs containing class 'image-header'. Ex i want the value text1 to be assigned on clicking add to compare of the div with id text1. However it assigns 'text1 text2 text3' to clgId.
Please help
I've created a JSFiddle with what I think is your desired functionality (I included a console log output in the script of the clgId variable):
http://jsfiddle.net/py38kuvv/
I replaced the parentsUntil function with the closest function (and replaced the individual-clg-container class selector):
var clgId = $(e.target).closest('.individual-college-container').find('.image-header').text();
and also updated your click handler:
$('.comparison').on( "click", function(e) {
In order to get a quicker response in future, posting a JSFiddle of what you have so far makes it easier for others to help :)
It is adding all of them because
var clgId = $(this).parentsUntil('.individual-clg-container').find('.image-header').text();
should be
var clgId = $(this).parentsUntil('.individual-college-container').find('.image-header').text();
I'm trying to clone an existing layer( $('.total-row').first() ), and then, remove all these layers ( $('.total-row') ), and add some customized layers from the cloned layer.
This is the JS-JQuery code:
$(document).on('click', '.playOnAP',function() {
var clon = $('.total-row').first().clone(true)
$('.total-row').remove();
$('#mp3-list ul li',this).each( function( index, element ) {
$('.jspPane').append(clon);
var totalTitle = $('.total-title').eq(index);
totalTitle.attr('src', $(this).text() ); //url canción
totalTitle.text($(this).text().slice(($(this).text().search("\/[^\/]+$")) + 1)); //canción
totalTitle.siblings( '.total-artist' ).text(artista); //artista
totalTitle.attr('imagen', caratula);
});
}
And here it is the html:
<div class="jspPane" style="padding: 0px; top: 0px; width: 300px;">
<div class="total-row can-play" onclick="" style="background-color: rgb(251, 251, 251);">
<div class="total-checked" onclick="" type="checkbox"></div>
<div class="total-not-playing total-playing"></div>
<div class="total-title" src="http://dcodedmagazine/wp-content/uploads/2013/12/Portishead_-_03_-_Undenied.ogg">titulo canción</div>
<div class="total-artist">artista</div>
<div style="clear:both;"></div>
</div>
<div class="total-row can-play" onclick="" style="background-color: rgb(251, 251, 251);">
<div class="total-checked" onclick="" type="checkbox"></div>
<div class="total-not-playing total-playing"></div>
<div class="total-title" src="http://dcodedmagazine/wp-content/uploads/2013/12/01.-Bored.ogg" imagen="<img src="http://dcodedmagazine/wp-content/uploads/2013/12/AlbumArt_6EC70FCA-D8CB-4DB1-955C-40F2E6CD2AB4_Large-150x150.jpg" class="attachment-100x100 wp-post-image" alt="AlbumArt_{6EC70FCA-D8CB-4DB1-955C-40F2E6CD2AB4}_Large" height="100" width="100">">01.-Bored.ogg</div>
<div class="total-artist">Deftones</div>
<div style="clear:both;"></div>
</div>
<div class="total-row can-play" onclick="" style="background-color: rgb(251, 251, 251);">...
</div>
The problem is that it is adding just the first $('#mp3-list ul li',this) element from $('.playOnAP'), while other sibling layers should be added.
As said in comment, cloning an element returns just a clone so when appending it multiple times, you are just moving the cloned element. As a simple fix, you could append a new clone:
$('.jspPane').append(clon.clone(true));