Functions only apply to the last div using .each function [duplicate] - javascript

This question already has answers here:
Event binding on dynamically created elements?
(23 answers)
Closed 2 years ago.
I've seen similar cases but I can't seem to figure it how on my case.
So I got 3 different divs all having the same class of "display-boxes". What I'm trying to do is to apply my functions for all 3 divs. At the moment it's only applying the function for the last div.
$(".display-boxes").each(function() {
var boxList = $(this).context;
function resizeTeasersTablet(teaserNumber, teaserCollection) {
if (teaserNumber == 1) {
teaserCollection[0].style.width = '73.834%';
teaserCollection[0].style.margin = '0 auto 40px';
for (let i = 1; i < teaserNumber; i++) {
teaserCollection[i].style.width = '25%';
}
} else if (teaserNumber == 2) {
for (let i = 0; i < teaserNumber; i++) {
teaserCollection[i].style.width = '50%';
}
} else if (teaserNumber == 4) {
for (let i = 0; i < teaserNumber; i++) {
teaserCollection[i].style.width = '50%';
}
} else if (teaserNumber == 5) {
for (let i = 0; i < teaserNumber; i++) {
teaserCollection[i].style.width = '50%';
}
} else if (teaserNumber == 3) {
for (let i = 0; i < teaserNumber; i++) {
teaserCollection[i].style.width = '33.33%';
}
}
}
function resizeTeasersMobile(teaserNumber, teaserCollection) {
for (let i = 0; i < teaserNumber; i++) {
teaserCollection[i].style.width = '100%';
}
}
document.addEventListener('DOMContentLoaded', function() {
if (window.innerWidth > 2024) {
displayTeasers(boxList.childElementCount, boxList.children);
} else if (window.innerWidth > 641) {
resizeTeasersTablet(boxList.childElementCount, boxList.children);
} else {
resizeTeasersMobile(boxList.childElementCount, boxList.children);
}
});
window.onresize = function() {
if (window.innerWidth > 2024) {
displayTeasers(boxList.childElementCount, boxList.children);
} else if (window.innerWidth > 641) {
resizeTeasersTablet(boxList.childElementCount, boxList.children);
} else {
resizeTeasersMobile(boxList.childElementCount, boxList.children);
}
};
});
<div id="section2" class="padding-margin-border column">
<div class="lobby-images-wrapper small-block-grid-1 display-boxes ongoingPromo">
<div class="display-box">
<div class="wrapper-lobby-image-item">
<span class="image-wrapper">
<a href="##Field.Teaser_Link##">##MarkComponentField(FieldPath+".Teaser_Image")##
<img src="##Field.Teaser_Image##" data-original="##Field.Teaser_Image##"/>
</a>
</span>
</div>
</div>
</div>
</div>
This is one of the 3 divs that I have since it won't let me post more code.

There are two problems here:
You've set window.onresize 3 times in a loop. Only the last assignment has an effect. You overwrite the previous two handlers. This is why your changes are only applied to your last div.
Document:DOMContentLoaded event is raised when the HTML document is fully loaded and parsed, which means either:
Your JavaScript code is run when the document is not loaded, and so your jQuery selector will not find your divs (it seems that it is not your case). This case happens when you load the code directly in a script at the beginning of the document's body.
Your JavaScript code is run after the document is loaded, so you'll find all your elements, but the event is already fired and your handler is never called. This case happens when you put the code inside an onload handler.
Your code is run after the divs are created but before the document is fully loaded. This case happens if you run the code before the end tag of </body> for example. This is the only case that works as expected. You better not put your code at such a risk! Your code should be robust and reliable.
The Fix
Here is how you can fix your issues (please pay close attention to my comments):
// Define your utility functions at the root level
function resizeTeasersTablet(teaserCollection) {
// No need to pass the collection size. It has a `length` property.
let width = undefined;
switch (teaserCollection.length) { // `swith`/`case` is simpler than those `if`s
case 1:
teaserCollection[0].style.width = '73.834%';
teaserCollection[0].style.margin = '0 auto 40px';
// The for loop is not needed. The length is 1!
break;
case 2:
case 4:
case 5:
width = '50%';
break;
case 3:
width = '33.33%';
break;
}
if (width)
for (let t of teaserCollection) // `for..of` is simpler
t.style.width = width;
}
function resizeTeasersMobile(teaserCollection) {
for (let t of teaserCollection)
t.style.width = '100%';
}
// The function name is clear
function resizeBasedOnWindowWidth(boxList) {
if (window.innerWidth > 2024)
displayTeasers(boxList.children);
else if (window.innerWidth > 641)
resizeTeasersTablet(boxList.children);
else
resizeTeasersMobile(boxList.children);
}
// The function name is clear
function resizeAllBoxListsBasedOnWindowWidth() {
$(".display-boxes").each(function () {
resizeBasedOnWindowWidth(this);
});
}
window.onresize = function () {
this.resizeAllBoxListsBasedOnWindowWidth(); // Just call the defined function.
}
$(function () { // See here: https://api.jquery.com/ready/
resizeAllBoxListsBasedOnWindowWidth(); // Do not repeat the code. Just call the function.
})
The principles I used in this code, are VERY important. I advise you to read the code multiple times :)
Good luck

Related

document.getElementById("#"); Not Working [duplicate]

This question already has answers here:
Why does jQuery or a DOM method such as getElementById not find the element?
(6 answers)
Closed 4 years ago.
I am trying to replace:
function checkValue() {
if (remainingTime < 10) {
document.getElementById("leading-zero").style.display = "inline";
} else {
document.getElementById("leading-zero").style.display = "none";
}
}
..with this:
var leadingZero = document.getElementById("leading-zero");
function checkValue() {
if (remainingTime < 10) {
leadingZero.style.display = "inline";
} else {
leadingZero.style.display = "none";
}
}
The first block of code works just fine but I don't want to be accessing the DOM everytime checkValue() is called (every half a second). That's why I attempted assigning the object (is it called an object??) to the variable named leadingZero. Why will this not work??
Instantiate leadingZero after document is loaded:
var leadingZero;
document.addEventListener("DOMContentLoaded", function(){
leadingZero = document.getElementById("leading-zero");
);
function checkValue() {
if (remainingTime < 10) {
leadingZero.style.display = "inline";
} else {
leadingZero.style.display = "none";
}
}

Best performance Javascript conditionals based on countdown timer

I am currently working on a project that is showing and displaying DOM elements based on a countdown timer. There is another function calling this one every second.
Here is a code sample:
function eventsOnTimer() {
let x = 1;
const interval = setInterval(() => {
if (x >= 0.0 && x < 30.0) {
document.getElementById('thing1').style.display = 'block';
document.getElementById('thing2').style.display = 'none';
}
else if (x >= 30.0 && x < 60.0) {
document.getElementById('thing1').style.display = 'none';
document.getElementById('thing2').style.display = 'block';
}
x++;
}, 1000);
}
I'm trying to increase performance, and I'm doing this by trying to reduce the number of DOM requests and looking at alternative ways to fire code based on the countdown timer.
Something like
function eventsOnTimer(id1, id2, ms) {
let toggle = false, thing1 = document.getElementById(id1), thing2 = document.getElementById(id2);
const interval = setInterval(() => {
if(toggle){
thing1.style.display = 'block';
thing2.style.display = 'none';
} else{
thing1.style.display = 'none';
thing2.style.display = 'block';
}
toggle = !toggle;
}, ms);
}
eventsOnTimer('thing1', 'thing2', 30000);
You can store all of nodes references before run your timer to dicrease DOM access time (getElementById).
After that, using className instead of style property will be faster. You juste need declared an specific CSS rule per state.
I propose to you an generic function to set automatically all of your nodes with the same CSS class name.
JS
var nodeArray = [];
var max_node = 2;
function storeNodeRef() {
for(var i =1; i <= max_node; i++) {
nodeArray.push( document.getElementById("thing"+i)); // Your nodes are declared with ID thing"X". "X" is a numeric value, set "max_node" with the latest "X" value.
}
eventsOnTimer();
}
function setNodeClass(nodeClassName) {
var i = 0;
while(i < max_node) {
nodeArray[i++].className = nodeClassName;
}
}
function eventsOnTimer() {
let x = 1;
const interval = setInterval(() => {
if (x==30 || x == 60) { // declare here your different state, you can use multiple if/elseif or multiple switch case.
setNodeClass('hide myClass'+x); // Param : new className
}
x++;
}, 1000);
}
storeNodeRef();
CSS
.process > div, .hide {display:none;}
#thing2.myClass30, #thing1.myClass60, .process > div.show {display:block; }
HTML EXAMPLE
<div class="process">
<div id="thing1" class="show" >Hello World 1</div>
<div id="thing2">Hello World 2</div>
</div>

how to attach click function to multiple divs without ID

I have a fade in function im trying to understand better. It works fine when I set up the
My question is if I have 8 links that already have the separate ID and class names how can I attach this function to each clickable link?
Is there a function to getElementbyClass or something and then just add the class to all my links?
here is my javascript:
var done = true,
fading_div = document.getElementById('fading_div'),
fade_in_button = document.getElementById('fade_in'),
fade_out_button = document.getElementById('fade_out');
function function_opacity(opacity_value) {
fading_div.style.opacity = opacity_value / 100;
fading_div.style.filter = 'alpha(opacity=' + opacity_value + ')';
}
function function_fade_out(opacity_value) {
function_opacity(opacity_value);
if (opacity_value == 1) {
fading_div.style.display = 'none';
done = true;
}
}
function function_fade_in(opacity_value) {
function_opacity(opacity_value);
if (opacity_value == 1) {
fading_div.style.display = 'block';
}
if (opacity_value == 100) {
done = true;
}
}
// fade in button
fade_in_button.onclick = function () {
if (done && fading_div.style.opacity !== '1') {
done = false;
for (var i = 1; i <= 100; i++) {
setTimeout((function (x) {
return function () {
function_fade_in(x)
};
})(i), i * 10);
}
}
};
// fade out button
fade_out_button.onclick = function () {
if (done && fading_div.style.opacity !== '0') {
done = false;
for (var i = 1; i <= 100; i++) {
setTimeout((function (x) {
return function () {
function_fade_out(x)
};
})(100 - i), i * 10);
}
}
};
Correcting the answer from BLiu1:
var fadeDivs = document.getElementsByClassName('fade');
for (var i=0, i<fadeDivs.length, i++){
// do stuff to all fade-divs by accessing them with "fadeDivs[i].something"
}
Have you considered using a javascript library like jQuery to manage this. They have some extensive, very easy to use "selectors" that allow you to easily get access to elements in the DOM and animate them with things like "fade ins" and "slides", etc. If you need more animations there are tons of plugins available for this. It also helps to deal with browser compatibility challenges too.
If you want to rely on pure JavaScript, you can use the document.getElementsByClassName() function defined here, but that function is only defined in IE9 and above as well as Safari, Chrome, FF, and Opera.
As said in the comments, there is a getElementsByClassName() method. Here is how you would use it.
for(var i=0; i<document.getElementsByClassName("fade").length; i++ ){
/*apply fade in function*/
}
I'm not sure whether getElementsByClassName() can detect one class name at a time. You might need regex for that.

Show/Hide & Mouseover Javascript

I been researching on Show/Hide javascript and pushed it further with a mouseover effect to achieve what I want. I've set up a Fiddle for better accessibility. However, I now want to push it by having up to 4 different text areas ("Click here for more information"), and each text area would have more hover text as I tried to show in the HTML code itself. The javascript that I used and edited now has "ID"s corresponding to "0" and "1" which wouldnt work for my current HTML code as it has funky names like "uu3308-10" (made with Adobe Muse). Now, I'm wonder what variables would I have to change within the Javascript to make it function properly and is there a way to compile this code so it works with at least 11 other "Click here for more information" points?
Note: The current javascript makes showMoreText2 appear under both showMoreText areas (would like to make only one hover text appear at a time).
CLICK HERE FOR THE FIDDLE -- > http://jsfiddle.net/TPLOR/vy6nS/
Thanks, I hope this was helpful enough. =)
kinda hackish: (see http://jsfiddle.net/vy6nS/30/ )
window.onload = function() {
var elems1 = document.getElementsByClassName("expander");
for (i = 0; i < elems1.length; i++) {
elems2 = elems1[i].childNodes;
for (x = 0; x < elems2.length; x++) {
if (elems2[x].className == "toggle") elems2[x].onclick = function() {
showMore(0, this);
};
else if (elems2[x].className == "showMoreText") {
elems2[x].onmouseover = function() {
showChilds("block", this);
};
elems2[x].onmouseout = function() {
showChilds("none", this);
};
}
}
}
};
function get_nextsibling(n) {
x = n.nextSibling;
while (x.nodeType != 1) {
x = x.nextSibling;
}
return x;
}
function showChilds(disp, elem) {
get_nextsibling(elem).style.display = disp;
}
function showMore(disp, elem) {
var children = elem.parentNode.childNodes;
for (i = 0; i < children.length; i++) {
if (disp == 0 && children[i].className == "showMoreText") {
children[i].style.display = children[i].style.display == "none" ? "block" : "none";
}
}
}​

Clear a non-global timeout launched in jQuery plug-in

I try to set a timeout on an element, fired with a jQuery plugin. This timeout is set again in the function depending on conditions. But, I want to clear this element's timeout before set another (if I relaunch the plug-in), or clear this manually.
<div id="aaa" style="top: 0; width: 100px; height: 100px; background-color: #ff0000;"></div>
Here's my code (now on http://jsfiddle.net/Ppvf9/)
$(function() {
$('#aaa').myPlugin(0);
});
(function($) {
$.fn.myPlugin = function(loops) {
loops = loops === undefined ? 0 : loops;
this.each(function() {
var el = $(this),
loop = loops,
i = 0;
if (loops === false) {
clearTimeout(el.timer);
return;
}
var animate = function() {
var hPos = 0;
hPos = (i * 10) + 'px';
el.css('margin-top', hPos);
if (i < 25) {
i++;
} else {
if (loops === 0) {
i = 0;
} else {
loop--;
if (loop === 0) {
return;
} else {
i = 0;
}
}
}
el.timer = window.setTimeout(function () {
animate();
}, 1000/25);
};
clearTimeout(el.timer);
//$('<img/>').load(function() {
// there's more here but it's not very important
animate();
//});
});
return this;
};
})(jQuery);
If I make $('#element').myPlugin();, it's launched. If I make it a second time, there's two timeout on it (see it by doing $('#aaa').myPlugin(0);
in console). And I want to be able to clear this with $('#element').myPlugin(false);.
What am I doing wrong?
EDIT :
SOLVED by setting two var to access this and $(this) here : http://jsfiddle.net/Ppvf9/2/
try saving the timeout handle as a property of the element. Or maintain a static lookup table that maps elements to their timeout handles.
Something like this:
el.timer = window.setTimeout(...);
I assume you need one timer per element. Not a single timer for all elements.

Categories