Loop through images and set background on hover in wix velo - javascript

I'm trying to set the background image of a wix strip to change images by looping through an array when it is hovered on. I do not see any errors being thrown in the editor, however it is still not working. This is my current code.
$w.onReady(function () {
var image1 = "https://workful.com/blog/wp-content/uploads/2019/10/Women-in-Small-Business.jpg";
var image2 = "https://data.europa.eu/sites/default/files/news/2020-25-3.jpg";
var image3 = "https://thumbor.forbes.com/thumbor/960x0/https%3A%2F%2Fblogs-images.forbes.com%2Fstartswithabang%2Ffiles%2F2017%2F10%2FTiny_bit_of_U.jpg";
var Background_imagez = [image1,image2,image3];
let playing = false;
let current = 0;
const run = () => {
setTimeout(() => {
$w('#columnStrip1').background.src = Background_imagez[current];
if (current < Background_imagez.length) {
current++;
} else { repeat() }
}, 500);
}
const repeat = () => {
current = 0;
run();
}
$w('#text59').onMouseIn(() => {
playing = true;
while (playing) { run() }
})
$w('#columnStrip1').onMouseOut(() => {
playing = false;
current = 0;
$w('#columnStrip1').background.src =
'https://wallpapercave.com/wp/wp2831956.png' ||
'https://images.unsplash.com/flagged/photo-1593005510329-8a4035a7238f?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8MXx8fGVufDB8fHx8&w=1000&q=80'; // Default
})
});

You are using for loops incorrectly.
for loops do not return a value so you cannot assign them to a variable. You also missed out adding for prior to assigning let index = 0
for (let index = 0; index < Background_imagez.length; index++) {
$w('#columnStrip1').background.src = Background_imagez[index];
}
I am not sure if the above would actually show anything other than the last image as it will probably loop through too fast for anyone to see but it should fix your Identifier expected error.
Please refer to the documentation on for loops on MDN

Related

'Not a Function' Error keep appearing when building a infinate scroll carousel and I dont know how to fix the problem [duplicate]

This question already has answers here:
What do querySelectorAll and getElementsBy* methods return?
(12 answers)
Closed 18 days ago.
I've been trying to build an infinite scroll blog carousel. I found a tutorial and followed it. Even watched it again to double-check that the code was correct but I keep getting an error in the console which I do not know how to fix.
Here's the code
var infinateScroll = document.querySelectorAll(".infinate-scroll");
var infinateScrollItems = document.querySelectorAll(".infinate-scroll .iscroll-item");
let clones = [];
let disableScroll = false;
let scrollWidth = 0;
let scrollPosition = 0;
let clonesWidth = 0;
function getScrollPosition() {
return infinateScroll.scrollLeft;
}
function setScrollPosition(e) {
infinateScroll.scrollLeft = e;
}
function getClonesWidth() {
clonesWidth = 0;
clones.forEach(clone => {
clonesWidth += clone.offsetWidth;
});
return clonesWidth;
}
function reCalc() {
scrollPosition = getScrollPosition();
scrollWidth = infinateScroll.scrollWidth;
clonesWidth = getClonesWidth();
if(scrollPosition <= 0) {
setScrollPosition(1);
}
}
function scrollUpdate() {
if(!disableScroll) {
scrollPosition = getScrollPosition();
if(clonesWidth + scrollPosition >= scrollWidth) {
setScrollPosition(1);
disableScroll = true;
}
else if (scrollPosition <= 0) {
setScrollPosition(scrollWidth - clonesWidth);
disableScroll = true;
}
}
if(disableScroll) {
window.setTimeout(() => {
disableScroll = false;
}, 40);
}
}
function onLoad() {
infinateScrollItems.forEach(ScrollItem => {
const cloneItems = ScrollItem.cloneNode(true);
infinateScroll.appendChild(cloneItems);
cloneItems.classList.add('js-clone');
});
clones = infinateScroll.querySelectorAll('.js-clones');
reCalc();
infinateScroll.addEventListener('scroll', () => {
window.requestAnimationFrame(scrollUpdate);
}, false);
window.addEventListener('resize', () => {
window.requestAnimationFrame(reCalc);
}, false);
}
window.onload = onLoad();
The error I'm getting is for this line of code; infinateScroll.appendChild(cloneItems);
The error says "infinateScroll.appendChild is not a function" and I don't know what I need to do or change to fix the issue. Any help on the matter would be very helpful. Thanks
when using .querySelectorAll, you should receive a nodeList as array as result. https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll
So you need to use the first item of infinateScroll, by using the array accessor [].
var infinateScroll = document.querySelectorAll(".infinate-scroll")[0];
alternatively, use querySelector() which returns a single Element object https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector
var infinateScroll = document.querySelector('.infinate-scroll');

I converted this forEach code to for loop and the code is not working

Clicking the red exit button removes the player from the forEach code, but not for loop.
You click the blue button, next you click the red exit button to remove the player.
How would I get the for loop code to work the same as the forEach code?
This code is working.
https://jsfiddle.net/n1t3kjdw/
function removePlayerHandler(evt) {
const el = evt.target;
const container = el.closest(".container");
const wrapper = container.querySelectorAll(".wrap");
wrapper.forEach(function(wrapper) {
if (wrapper.player) {
return removePlayer(wrapper);
}
});
}
What did I do wrong here? https://jsfiddle.net/rbwsL8hf/
Why is this code not working, what needs to be fixed?
function removePlayerHandler(evt) {
const el = evt.target;
const container = el.closest(".container");
const wrappers = container.querySelectorAll(".wrap"); {
for (let i = 0; i < wrappers[i].length; i++) {
if (wrappers[i].player) {
return removePlayer(wrappers[i]);
}
}
}
}
In your changed code you have made the iteration condition check for wrappers[i].length instead of what I believe was intended wrappers.length. Otherwise, you are for each iteration checking if i is smaller than the i-th wrapper's length which may or may not be defined.
function removePlayerHandler(evt) {
const el = evt.target;
const container = el.closest(".container");
const wrappers = container.querySelectorAll(".wrap"); {
for (let i = 0; i < wrappers[i].length; i++) { // HERE
if (wrappers[i].player) {
return removePlayer(wrappers[i]);
}
}
}
}
Complementing #Salim answer, there is also an extra bracket in your code, try this:
function removePlayerHandler(evt) {
const el = evt.target;
const container = el.closest(".container");
const wrappers = container.querySelectorAll(".wrap");
for (let i = 0; i < wrappers.length; i++) {
if (wrappers[i].player) {
return removePlayer(wrappers[i]);
}
}
}

How do I iterate over an async function without completely skipping over all the steps in the function?

Here is the link to my repo's github page, so you can properly see what I mean.
I am currently having an issue with my triviaGame function when trying to make it recursive, but it's sort of "backfiring" on me in a sense.
You'll notice after you answer the first question, everything seems fine. It goes to the next question fine. After that though, it seems like the iterations of it double? The next answer it skips 2. After that, 4. And finally the remaining 2 (adding up to 10, due to how I am iterating over them).
How might I be able to correctly iterate over a recursive function, so it correctly calls all 10 times, and then returns when it is done?
Been struggling with this for hours, and just can't seem to get it to work. My javascript code is below, sorry for any headaches that it may give you. I know I make some questionable programming decisions. Ignore some of the commented out stuff, it's not finished code yet. I'm a beginner, and hope that once I learn what's going on here it will stick with me, and I don't make a stupid mistake like this again.
const _URL = "https://opentdb.com/api.php?amount=1&category=27&type=multiple";
const _questionHTML = document.getElementById("question");
const _answerOne = document.getElementById("answer-1");
const _answerTwo = document.getElementById("answer-2");
const _answerThree = document.getElementById("answer-3");
const _answerFour = document.getElementById("answer-4");
const btns = document.querySelectorAll("button[id^=answer-]");
var runCount = 1;
var correct = 0;
// Credits to my friend Jonah for teaching me how to cache data that I get from an API call.
var triviaData = null;
async function getTrivia() {
return fetch("https://opentdb.com/api.php?amount=1&category=27&type=multiple")
.then((res) => res.json())
.then((res) => {
triviaData = res;
return res;
});
}
// anywhere I want the trivia data:
// const trivia = await getTrivia() --- makes the call, or uses the cached data
const shuffleArray = (array) => {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
const temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
};
async function triviaGame() {
const trivia = await getTrivia();
async function appendData() {
let totalAnswers = [
...trivia.results[0].incorrect_answers,
trivia.results[0].correct_answer,
];
// Apparently I need 2 different arrays to sort them because array variables are stored by reference? Learn something new everyday I guess.
let totalAnswers2 = [...totalAnswers];
let sorted = shuffleArray(totalAnswers2);
// Ensures the proper symbol shows instead of the HTML entities
const doc = new DOMParser().parseFromString(
trivia.results[0].question,
"text/html"
);
_questionHTML.textContent = doc.documentElement.textContent;
console.log(trivia.results[0].correct_answer, "- Correct Answer");
// Appends info to the DOM
_answerOne.textContent = sorted[0];
_answerTwo.textContent = sorted[1];
_answerThree.textContent = sorted[2];
_answerFour.textContent = sorted[3];
}
async function checkAnswer() {
btns.forEach((btn) => {
btn.addEventListener("click", (event) => {
console.log(runCount);
if (event.target.textContent === trivia.results[0].correct_answer) {
event.target.style.backgroundColor = "#52D452";
// Disables all buttons after one has been clicked.
btns.forEach((btn) => {
btn.disabled = true;
});
setTimeout(() => {
if (runCount === 10) {
return;
}
runCount++;
correct++;
btns.forEach((btn) => {
btn.disabled = false;
});
btn.style.backgroundColor = "";
document.getElementById(
"amount-correct"
).textContent = `${correct}/10`;
triviaGame();
}, 2000);
} else {
event.target.style.backgroundColor = "#FF3D33";
btns.forEach((btn) => {
btn.disabled = true;
});
// document.getElementById("correct-text").textContent =
// trivia.results[0].correct_answer;
// document.getElementById("correct-answer").style.visibility =
// "visible";
setTimeout(() => {
if (runCount === 10) {
return;
}
// document.getElementById("correct-answer").style.visibility =
// "hidden";
btns.forEach((btn) => {
btn.disabled = false;
btn.style.backgroundColor = "";
});
runCount++;
triviaGame();
}, 3500);
}
});
});
}
checkAnswer();
appendData();
}
triviaGame();
Any/All responses are much appreciated and repsected. I could use any help y'all are willing to give me. The past 6 hours have been a living hell for me lol.
It's skipping questions once an answer is clicked because every time a button is clicked, another event listener is added to the button, while the original one is active:
On initial load: triviaGame() runs which makes checkAnswer() run which adds event listeners to each of the buttons.
Event listeners on buttons: 1.
Answer button is clicked, triviaGame() runs which makes checkAnswer() run which adds event listeners to each of the buttons.
Event listeners on buttons: 2.
Answer button is clicked, triviaGame() runs twice (from the 2 listeners attached) which makes checkAnswer() run twice where both invocations adds event listeners to each of the buttons.
Event listeners on buttons: 4.
etc.
To fix this, I moved the content of checkAnswer() outside of any functions so it only ever runs once. However, doing this, it loses reference to the upper scope variable trivia. To resolve this, I used the triviaData variable instead which checkAnswer() would have access to and I change references in appendData() to match this. Now, triviaGame() function only exists to call appendData() function inside it; there is little point in this so I merge the two functions together into one function, instead of two nested inside each other.
const _URL = "https://opentdb.com/api.php?amount=1&category=27&type=multiple";
const _questionHTML = document.getElementById("question");
const _answerOne = document.getElementById("answer-1");
const _answerTwo = document.getElementById("answer-2");
const _answerThree = document.getElementById("answer-3");
const _answerFour = document.getElementById("answer-4");
const btns = document.querySelectorAll("button[id^=answer-]");
var runCount = 1;
var correct = 0;
// Credits to my friend Jonah for teaching me how to cache data that I get from an API call.
var triviaData = null;
async function getTrivia() {
return fetch("https://opentdb.com/api.php?amount=1&category=27&type=multiple")
.then((res) => res.json())
.then((res) => {
triviaData = res;
return res;
});
}
// anywhere I want the trivia data:
// const trivia = await getTrivia() --- makes the call, or uses the cached data
const shuffleArray = (array) => {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
const temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
};
async function appendData() {
triviaData = await getTrivia();
let totalAnswers = [
...triviaData.results[0].incorrect_answers,
triviaData.results[0].correct_answer,
];
// Apparently I need 2 different arrays to sort them because array variables are stored by reference? Learn something new everyday I guess.
let totalAnswers2 = [...totalAnswers];
let sorted = shuffleArray(totalAnswers2);
// Ensures the proper symbol shows instead of the HTML entities
const doc = new DOMParser().parseFromString(
triviaData.results[0].question,
"text/html"
);
_questionHTML.textContent = doc.documentElement.textContent;
console.log(triviaData.results[0].correct_answer, "- Correct Answer");
// Appends info to the DOM
_answerOne.textContent = sorted[0];
_answerTwo.textContent = sorted[1];
_answerThree.textContent = sorted[2];
_answerFour.textContent = sorted[3];
}
btns.forEach((btn) => {
btn.addEventListener("click", (event) => {
console.log(runCount);
if (event.target.textContent === triviaData.results[0].correct_answer) {
event.target.style.backgroundColor = "#52D452";
// Disables all buttons after one has been clicked.
btns.forEach((btn) => {
btn.disabled = true;
});
setTimeout(() => {
if (runCount === 10) {
return;
}
runCount++;
correct++;
btns.forEach((btn) => {
btn.disabled = false;
});
btn.style.backgroundColor = "";
document.getElementById(
"amount-correct"
).textContent = `${correct}/10`;
appendData();
}, 2000);
} else {
event.target.style.backgroundColor = "#FF3D33";
btns.forEach((btn) => {
btn.disabled = true;
});
// document.getElementById("correct-text").textContent =
// trivia.results[0].correct_answer;
// document.getElementById("correct-answer").style.visibility =
// "visible";
setTimeout(() => {
if (runCount === 10) {
return;
}
// document.getElementById("correct-answer").style.visibility =
// "hidden";
btns.forEach((btn) => {
btn.disabled = false;
btn.style.backgroundColor = "";
});
runCount++;
appendData();
}, 3500);
}
});
});
appendData();
<div id="amount-correct"></div>
<h1 id="question"></h1>
<button id="answer-1"></button>
<button id="answer-2"></button>
<button id="answer-3"></button>
<button id="answer-4"></button>

How to stop setTimeout loop if condition is met

I am making a screensaver that displays the elements inside an array with a delay between each iteration, to display the elements slowly one by one. My "onmousemove" event successfully removes the screensaver from the page, but the for loop in my startScreensaver() function keeps running when it should in fact break. What am I doing wrong?
see JSfiddle https://jsfiddle.net/m60k75jf/
const idletime = 2;
const screenSaver = document.getElementById("screensaver");
const stars = Array.from(document.querySelectorAll(".star"));
let mousetimeout;
let screensaverActive = false;
window.addEventListener("mousemove", (e) => {
clearTimeout(mousetimeout);
if (screensaverActive) {
stopScreensaver();
} else {
mousetimeout = setTimeout(function () {
startScreensaver();
}, 1000 * idletime);
}
});
function stopScreensaver() {
screensaverActive = false;
stars.forEach((star) => {
star.classList.remove("is--visible");
});
screenSaver.classList.remove("is--active");
}
function startScreensaver() {
screensaverActive = true;
screenSaver.classList.add("is--active");
for (let index = 0; index < stars.length; index++) {
if (screensaverActive) {
setTimeout(function () {
stars[index].classList.add("is--visible");
}, 2000 * index);
} else {
break;
}
}
}
You can't break the loop like that. Your loop creates all these setTimeout immediately. Your condition will just never trigger. What you'll need to do is clear out all those setTimeout. You can push them into an array.
let animationTimeouts;
function startScreensaver() {
animationTimeouts = [];
screensaverActive = true;
screenSaver.classList.add("is--active");
for (let index = 0; index < stars.length; index++) {
if (screensaverActive) {
animationTimeouts.push(setTimeout(function () {
stars[index].classList.add("is--visible");
}, 2000 * index));
} else {
break;
}
}
}
Then clear them out on stopScreensaver
function stopScreensaver() {
screensaverActive = false;
if(animationTimeouts) animationTimeouts.forEach(clearTimeout);
stars.forEach((star) => {
star.classList.remove("is--visible");
});
screenSaver.classList.remove("is--active");
}
You might also want to reconsider moving your CSS transition to .star.is--visible

Profile scroller app: how to go back to previous profile? (JS)

I am trying to build a profile scroller-type app, and I am currently stuck on how I'm supposed to go back to the previous profile. See the code below:
let profiles = profileIterator(profiles); // array of objects stored in a separate .js file
// Next Event
next.addEventListener("click", nextProfile);
// Next Profile Display
function nextProfile() {
const currentProfile = profiles.next().value;
if(currentProfile !== undefined) {
name.innerHTML = `<h4>${currentProfile.name}</h4>`; // content on HTML page that will be changed
text.innerHTML = `<p>${currentProfile.info}</p>`
img.innerHTML = `<img src="${currentProfile.img}">`
next.innerHTML = 'Next';
previous.style.display = "inline-block";
previous.innerHTML = 'PREVIOUS';
} else {
window.location.reload();
}
}
function previousProfile() {
const currentProfile = profiles.previous().value;
if(currentProfile !== undefined) {
name.innerHTML = `<h4>${currentProfile.name}</h4>`;
text.innerHTML = `<p>${currentProfile.info}</p>`
img.innerHTML = `<img src="${currentProfile.img}">`
} else {
window.location.reload();
}
}
// Profile Iterator
function profileIterator(profiles) {
let nextIndex = 0;
return {
next: function() {
return nextIndex < profiles.length
? { value: profiles[nextIndex++], done: false }
: { done: true }
},
previous: function() {
return nextIndex < profiles.length
? { value: profiles[nextIndex--], done: false }
: { done: true}
}
};
}
It seemed pretty straightforward to me at first, but apparently, I am wrong as whenever I click the Previous button, instead of going through the previous profiles, it instead jumbles them up in no particular order, missing one of them entirely as well.
Any advice? Is there a way to set an HTML5 video element's attributes from Batch?
are you sure that when returning .previous() you should check if nextIndex < profiles.length? For my first glance, there is no way for that to be false and maybe it's the source of your problems.

Categories