How can i toggle innerHTML inside a setInterval() function with JS - javascript

I am trying to alternate the innerHTML of a at set Intervals.
Dear friends,
I am new to coding. I have created a div with an image, and a < p > element (that includes a < span >.
I have assigned two classes to the div, and I want it to alternate between the 2 classes at set intervals.
In addittion, I am trying to toggle the text inside the span, by using innerHTML.
So far I have managed succesfully to toggle the class, but I can't make the innerHTML to work.
I have the following code:
if(categProducts[idx].discount && categProducts[idx].low){
var Interval = setInterval(
function changeClass(){
document.getElementById('myDiv').classList.toggle("low");
},3000
)
var Interval2= setInterval(function changeText(){
var x=document.getElementById('mySpan').innerHTML
if (x==="<br> Only Few Cakes Left!!"){
x.innerHTML="<br> Discount! Best Price!!"
}
else {
x="<br> Only Few Cakes Left!!"
}
console.log(x)
}, 3000)
}
So far, the innerHTML of the only toggles once, and then it doesn't change again.
I can't make it work and I don't understand why.
The rest of the code is the following:
for (let idx in categProducts){
if (categProducts[idx].category==="cakes") {
const parentElement=document.getElementById("divCakes")
const myDiv=document.createElement("div")
parentElement.appendChild(myDiv)
myDiv.className="product"
const myImg=document.createElement("img")
myImg.src=categProducts[idx].imageURI
myImg.alt=categProducts[idx].alt
myDiv.appendChild(myImg)
myDiv.id="myDiv"
const myP=document.createElement("p")
myP.innerHTML=categProducts[idx].name
myDiv.appendChild(myP)
mySpan=document.createElement("span")
myP.appendChild(mySpan)
mySpan.id="mySpan"

IMO, you should define your desired classes and content inside arrays. Works for two, and more.
const changeContent = (() => {
const classes = ['first-class', 'second-class'];
const spanText = ['first text', 'second text'];
let index = 0;
return function() {
document.getElementById('mySpan').innerHTML = "";
document.getElementById('myDiv').classList = "";
document.getElementById('mySpan').innerHTML = spanText[index];
document.getElementById('myDiv').classList = classes[index];
index++;
if(index === classes.length) {
index = 0;
}
}
})();
setInterval(changeContent, 3000);
This functions uses closures to define global variables.

Related

addEventListener callback not taking new values from <select> element input

Trying to create a button that update the H1 number based on how many times I pressed it. And I added a select element to select the maximun number H1 can go to.
Why does it not work when I define the playRounds variable outside of the addeventlistener call back. but works when I define it in the callback.
this works:
const h1 = document.querySelector("h1");
const roundsToPlay = document.querySelector("select");
const button = document.querySelector("button");
let buttonPressNumber = 0;
button.addEventListener("click", () => {
let playRounds = roundsToPlay.value
if (buttonPressNumber < playRounds) {
buttonPressNumber += 1;
h1.innerText = buttonPressNumber;
}
})
this Does not work:
const h1 = document.querySelector("h1");
const roundsToPlay = document.querySelector("select");
const button = document.querySelector("button");
let buttonPressNumber = 0;
let playRounds = roundsToPlay.value
button.addEventListener("click", () => {
if (buttonPressNumber < playRounds) {
buttonPressNumber += 1;
h1.innerText = buttonPressNumber;
}
})
When I define the "playRounds" varaible equals to an acutal number outside of the callback, it works too. So it shouldn't be a scope problem.
eg:
let playRounds = 5;
Does the callback in addEventListner not take updates from the change of value in <select> element when it's defined outside of the callback?
When you do let playRounds = roundsToPlay.value, the value (for instance, "4") is copied from roundsToPlay.value to playRounds, but there is no ongoing connection between roundsToPlay.value and playRounds afterward. JavaScript does not have references to variables or object properties (other than conceptually within the specification, but that isn't relevant to the code you write, just how the specification is written).
If you want the up-to-date value, you have to retrieve it in the event handler as you do in your working example.
playRounds is only assigned once, so assuming the first <option> in your <select> is 1 then playRounds will be 1 forever as it's never updated.
Instead you need to compare buttonPressNumber to the current value of roundsToPlay
if (buttonPressNumber < roundsToPlay.value) {
buttonPressNumber += 1;
h1.innerText = buttonPressNumber;
}
})
https://jsfiddle.net/vzw6e9my/1/

How can I test a JS function without input (with Jest)

I made a simple website who contains a sidebar. On click on the Sidebar Icon, it should open or close. The following code that I wrote does extactly that.
/** toggleStatus is a anonymous function and can be called with its name */
let navStatus = false;
let toggleStatus = function () {
let getSidebar = document.querySelector(".sidebar");
let getSidebarUl = document.querySelector(".sidebar ul");
let getSidebarLinks = document.querySelectorAll(".sidebar a");
if (navStatus === false) {
// if Sidebar is closed
closeMenu();
getSidebarUl.style.visibility = "visible";
getSidebar.style.width = "272px"; // change width of sidebar so the content can fit
let arrayLength = getSidebarLinks.length;
for (let i = 0; i < arrayLength; i++) {
// Smake every List item Visible
getSidebarLinks[i].style.opacity = "1";
}
navStatus = true;
} else {
// if Sidebar is open
getSidebarUl.style.visibility = "hidden";
getSidebar.style.width = "50px"; // change width of sidebar to the base Design
let arrayLength = getSidebarLinks.length;
for (let i = 0; i < arrayLength; i++) {
// make every List item invisible
getSidebarLinks[i].style.opacity = "0";
}
navStatus = false;
}
};
Now I have to write a test with Jest for this function, but I have no idea where to begin with. I cant feed it with any Input and compare the expected Output. Does anyone have some hints for a newbie?
Thank you!
If your functions don't have explicit output it doesn't mean it does nothing. The function has some "behavior" and in your case, it does some DOM manipulations. So you should test for it.
You have two options to approach it:
Mock needed DOM API functions and expect mocked object's props in the shape you like.
Use other tools to render actual HTML with snapshot testing.

Array of elements with certain class becomes undefined in setTimeout

I am using a JavaScript file to hide various div's all with the same class name, the program gets as far as hiding them but when I want them to be visible after "x" number of seconds the array of elements becomes undefined and has length 0, not sure what is going on. I have tried changing "lowerDash" to a global variable to no avail.
function newBaseHandler(){
if (document.getElementById("Base6").innerHTML == `<br><p>Create new base</p>`) {
let lowerDash = document.getElementsByClassName("LowerDashboard");
let message = document.getElementById("Message");
for (let button of lowerDash) {
button.style.visibility = "hidden";
}
message.innerHTML = `<br><p>Base created successfully</p>`;
setTimeout(function() {
message.innerHTML = ``;
console.log(lowerDash.length);
for (let button of lowerDash) {
button.style.visibility = "visible";
}
}, 1000);
}
}
If you want them to become visible again after a timeout, try
button.style.visibility = "visible"
in your for loop insie setTimeout

Creating a closure for .setTimeout() inside a for loop

I am trying to write a javascript program which stores the value from an input element in an array when a button is clicked. The array is the split and each individual letter added to a span element and then appended to the document. The idea is to create a typing effect using setTimeout.
I am running into an issue creating a closure within the loop, so currently the setTimeout function always returns the final value of the iteration.
The function in question is at the bottom of the code block and called addTextToBoard();
var noteButton = document.querySelector('[data-js="button"]');
noteButton.addEventListener("click",function() {
var messageIn = document.querySelector('[data-js="input"]');
var message = messageIn.value;
postToBoard(message);
});
function postToBoard(val) {
var noteBoard = document.querySelector('[data-js="noteboard"]');
var newElement = document.createElement('div');
newElement.classList.add('noteboard__item');
noteBoard.appendChild(newElement);
setTimeout(function(){
newElement.classList.add('active');
}, 200);
addTextToBoard(newElement, val);
}
function addTextToBoard(el, val) {
var wordArray = val.split('');
for(i = 0; i < wordArray.length; i++) {
var letter = document.createElement('span');
letter.innerHTML = wordArray[i];
setTimeout(function(x){
return function() {}
el.appendChild(letter);
}(i),1000);
}
}
I believe I am close, I'm just not fully understanding the syntax for creating the closure. If someone could give poke in the right direction, without necessarily giving the full solution that would be great.
I essentially tried to paste in the following code snippet from here but I've missed something somehwere along the way!
setTimeout(function(x) { return function() { console.log(x); }; }(i), 1000*i);
Best,
Jack
You are close.
Since the "letter" variable changes, you'll add only the last letter over and over again. You need to "save" the current letter on the setTimeout() callback function, One way to go is like this:
function appendMyLetter(letter) {
return(function() {
el.append.Child(letter);
});
}
function addTextToBoard(el, val) {
var wordArray = val.split('');
for(i = 0; i < wordArray.length; i++) {
var letter = document.createElement('span');
letter.innerHTML = wordArray[i];
setTimeout(appendMyLetter(letter), 1000);
}
}
This way, the appendMyLetter() function gets called with a different parameter (one for each letter) and returns a function with the correct "stored" value to be called by setTimeout().
EDIT
Looking at your setTimeout() code closely
setTimeout(function(x){
return function() {}
el.appendChild(letter);
}(i),1000);
It would work fine, if you used the proper parameters and used the appendChild() inside the returned function, like so:
setTimeout(function(x){
return(function() {
el.appendChild(x);
});
}(letter),1000);
You can create an immediately-invoked function expression IIFE to create a closure
function addTextToBoard(el, val) {
var wordArray = val.split('');
for(i = 0; i < wordArray.length; i++) {
(function(index) {
var letter = document.createElement('span');
letter.innerHTML = wordArray[i];
setTimeout(function(){
el.appendChild(letter);
},1000);
})(i);
}
}
I dont know if this will work but here you go a slight change in operator:
letter.innerHTML += wordArray[i];
if you dont get the effect you imagined you will get you better try to increment the timer by i like this
setTimeout(function(){
...
},1000*i);

Loop through js array continually with pauses

I want to loop over an array continuously on click. Extra props if you can work in a delay between class switches :-)
I got this far:
// Define word
var text = "textthing";
// Define canvas
var canvas = 'section';
// Split word into parts
text.split();
// Loop over text
$(canvas).click(function() {
$.each(text, function(key, val) {
$(canvas).removeAttr('class').addClass(val);
});
});
Which is not too far at all :-)
Any tips?
The following will wait until you click the selected element(s) in the var el. In this example var el = $('section') will select all <section>...</section> elements in your document.
Then it will start cycling through the values in cssClassNames, using each, in turn as the css class name on the selected element(s). A delay of delayInMillis will be used between each class change.
var cssClassNames = ['c1', 'c2', 'c3'];
var el = $('section');
var delayInMillis = 1000;
// Loop over text
el.click(function() {
var i = 0;
function f() {
if( i >= cssClassNames.length ) {
i = 0;
}
var currentClass = cssClassNames[i];
i += 1;
el.removeClass().addClass(currentClass);
setTimeout(f, delayInMillis);
}
f();
});
I believe you want a delay of X milliseconds between removing a class and adding a class. I'm not sure that you have to have the lines marked // ? or even that they do the job, but what you do have to have is a way to get the value's into the function. Also, the setTimeout anon function might not actually need the parameters, but it should give you an idea.
$(canvas).click(function() {
$.each(text, function(key, val) {
$(canvas).removeAttr('class')
var $canvas = $(canvas) //?
var class_val = val //?
setTimeout(function ($canvas, class_val) {
$canvas.addClass(class_val);
}, 2000);
});
});
Edit: I'd do this instead
function modify_element($element, class_name){
$element.removeClass('class');
setTimeout(function ($element) {
$element.addClass(class_name);
}, 1000);
//adds the class 1 second after removing it
}
$(canvas).click(function() {
$.each(text, function(key, val) {
setTimeout(modify_element($(canvas), val),2000);
//this will loop over the elements with 2 seconds between elements
});
});
"loop over an array continuously" this sounds like a infinite loop, I don't think you want that. About pausing the loop, this is possible, you can use this

Categories

Resources