Change multiple image src with jquery in a circular fashion - javascript

I have 3 img tag in my html file. I want to change all of the 3 img's src with button click from an array where I stored 9 images src link.
So when the page loads for the very first time it will show first 3 images[0,1,2]. Then on each next-btn click it will change images to the next 3 [3,4,5], [6,7,8]. And when the prev-btn is clicked it will go back to 3 images.
please help me with that. and please suggest me is there any other way(better) around it?
Here is what I've tried so far:
const $img1 = $("#img1")
const $img2 = $("#img2")
const $img3 = $("#img3")
const $nextBtn = $("#next-btn")
const $prevBtn = $("#prev-btn")
$.get("https://someApiLink.com/all.json", function (characters) {
const charactersList = []
for (let i=0; i<characters.length; i++) {
// here casts is another array to match predefined characters with the
// character from the api
if (casts.includes(characters[i].name)) {
charactersList.push(characters[i])
}
}
$img1.attr("src", charactersList[0].image)
$img2.attr("src", charactersList[1].image)
$img3.attr("src", charactersList[2].image)
// it will only switch back and forth 2 images on the first img tag
$nextBtn.on("click", function () {
const src = ($img1.attr("src") === charactersList[0].image)
? charactersList[1].image
: charactersList[0].image;
$img1.attr("src", src)
})
})

You'll need two things:
A function that sets images based on an index.
An index to keep track on where to in charactersList to start counting from.
With the function you can render you images based on a starting point with +1 and +2 index. So let's say you start at 2, then 3 and 4 will also be rendered.
The nextBtn should increment the index with +3 so that the next 3 images will be rendered.
The prevBtn should decrement the index with -3 so that the previous 3 images will be rendered.
const $img1 = $("#img1")
const $img2 = $("#img2")
const $img3 = $("#img3")
const $nextBtn = $("#next-btn")
const $prevBtn = $("#prev-btn")
$.get("https://someApiLink.com/all.json", function (characters) {
const charactersList = []
for (let i=0; i<characters.length; i++) {
if (casts.includes(characters[i].name)) {
charactersList.push(characters[i])
}
}
// Index to start counting from.
let characterIndex = 0;
/**
* Sets three images based in the given index.
* Indexes will be counted upwards starting with the index.
* #param {number} index
*/
const setThreeImageFromIndex = index => {
$img1.attr("src", charactersList[index].image)
$img2.attr("src", charactersList[index + 1].image)
$img3.attr("src", charactersList[index + 2].image)
});
// Set images for the first time.
setThreeImageFromIndex(characterIndex);
// Go to next 3.
$nextBtn.on("click", function () {
// Don't go over the length of the characterList.
if (characterIndex + 3 < charactersList.length) {
characterIndex += 3;
setThreeImageFromIndex(characterIndex);
}
});
// Go to previous 3.
$prevBtn.on("click", function () {
// Don't go below 0.
if (characterIndex - 3 >= 0) {
characterIndex -= 3;
setThreeImageFromIndex(characterIndex);
}
});
})

You can store a sort of "page number" for example page to keep track of what page are they on and increase/decrease it on each "press".
let page = 0;
const imgInSet = 3;
const reloadImages = ()=>{
const imgSet = Math.abs(page); // we get abs if someone do more previous than nexts
$img1.attr("src", characterList[imgSet*imgInSet].image); // get first image
$img2.attr("src", characterList[imgSet*imgInSet+1].image); // get second image
$img3.attr("src", characterList[imgSet*imgInSet+2].image); // get third image
}
$nextBtn.on("click", ()=>reloadImages(++page)); // do reloadImages after adding 1 to roll
$prevBtn.on("click", ()=>reloadImages(--page);) // do reloadImages after removing 1 from roll
You could add a numberOfPages value to restrict the number of available pages like that:
let page = 0;
const imgInSet = 3;
const numberOfPages = Math.floor(characterList.length/3);
const reloadImages = ()=>{
const imgSet = Math.abs(page)%numberOfPages; // we get abs if someone do more previous than nexts
$img1.attr("src", characterList[imgSet*imgInSet].image); // get first image
$img2.attr("src", characterList[imgSet*imgInSet+1].image); // get second image
$img3.attr("src", characterList[imgSet*imgInSet+2].image); // get third image
}
Those two swap previous/next buttons when they get to the negative numbers by doing it like that:
let page = 0;
const imgInSet = 3;
const numberOfPages = Math.floor(characterList.length/3);
const reloadImages = ()=>{
const imgSet = page<0?numberOfPages+page%imgInSet:page)%numOfPages;
$img1.attr("src", characterList[imgSet*imgInSet].image);
$img2.attr("src", characterList[imgSet*imgInSet+1].image);
$img3.attr("src", characterList[imgSet*imgInSet+2].image);
}
$nextBtn.on("click", ()=>reloadImages(++page));
$prevBtn.on("click", ()=>reloadImages(--page);)
This answer is for more "universal solution" where you won't need to change too much with each new img that you would need to be cycled through.

Related

How to render elements based on a given value in React JS?

I want to render some stars based on a specific value, and this is what I have done so far
const Rating = ({ value }) => {
const renderStars = () => {
let stars = []; // This array should contain all stars either full, half or empty star
for (let i = 0; i < value; i++) {
if (value % 1 !== 0) {
// If the value has decimal number
} else {
// If the value has NO decimal number
}
}
return stars?.map((star) => star); // Mapping the stars array
};
return <div className="rating">{renderStars()}</div>;
};
export default Rating;
Now I have 3 icons: a full star, a half star, and an empty star. Let's say the rating value is 3.5, so what I want is to push to the stars array 3 full stars 1 half star and 1 empty star so that will be 5 stars in total. And then I can map through the array and render all the stars.
You can loop through up until your value as you're currently doing, where for each iteration you push a full star, and then after the loop is complete, check if value is a decimal to determine if you should push an additional half star:
const STAR_COUNT = 5;
const Rating = ({ value }) => {
const stars = Array.from({length: STAR_COUNT}, () => <EmptyStar />);
let i;
for (i = 0; i < value; i++) { // this will loop Math.floor(value) times
stars[i] = <FullStar />;
}
if (value % 1 != 0) // if value is a decimal, add a half star
stars[i-1] = <HalfStar />;
return <div className="rating">{stars}</div>;
};
I would also suggest wrapping this component in a call to React.memo() so that the for loop logic only runs when your value prop changes, and not what the parent rerenders.
Another, perhaps more concise way, is to use some array methods to help, such as .fill() to populate an array firstly with empty stars, then replace those empty stars up to a given index based on your value, and finally add a half star if required:
const STAR_COUNT = 5;
const Rating = ({ value }) => {
const stars = Array(STAR_COUNT).fill(<EmptyStar />).fill(<FullStar />, 0, Math.floor(value));
if (value % 1 != 0) // if value is a decimal, add a half star
stars[Math.floor(value)] = <HalfStar />;
return <div className="rating">{stars}</div>;
};
Try this in your code
let rating = 3.5;
let ratingCount = 0
for(let i=1; i<=5; i++){
if(i<=rating){
console.log("full")
ratingCount++
}
}
if(rating-Math.floor(rating) !== 0){
console.log("Half")
ratingCount++
}
for(let i=ratingCount ; i<5; i++){
console.log("empty")
}

Find index of every n number in array

I have a big data which I load and show on the page in chunks. Initially I load the first 50 items and after that on button click I get the next 25.
So I have 50 items on initial page load. After button click I fetch +25 items and now I have 75.
On the second button click I get the next +25 items and now I have 100 etc.
I need to find always the number that is ten places before the last number in the list.
When my list size is 50 - I need to get the index of the 40-th element in the list.
When my list size is 75 I need to get the index of the 65-th element in the list,
When my list size is 100 I need to get the index of the 90-th element in the list.
** Simulation **
let arr = [];
for (let i = 0; i < 50; i++) {
arr.push(i + 1)
}
let btn = document.getElementById('btn');
btn.addEventListener('click', () => {
for (let i = 0; i < 25; i++) {
arr.push(i + 1);
}
console.log(arr);
})
<button id="btn">Add items</button>
How can be this done?
Is there better way than
let index = arr[arr.length - 11];
console.log(index);
You're referencing the element the right way, you can create an arrow function that fetches the 10th last element (or undefined in case the list is smaller then 10)
var smallArr = [1,2,3,4,5,6,7,8,9,10,11,12];
var bigArr = [1,2,3,4,5,6,7,8];
const tenthLastElement = (array) => array[array.length - 11];
console.log(tenthLastElement(smallArr));
console.log(tenthLastElement(bigArr));

Stack of cards effect: Change zIndex to highest of all similar divs when dragging

I'm trying to put a div on top of all the similar divs using z-index when start dragging. My attempt is kinda working, but when clicking on a the other div of the 3, that one doesn't go on top but stays under.
// Handle start moving
const handleMoveStart = () => {
const currentIndex = parseInt(document.defaultView.getComputedStyle(card).getPropertyValue('z-index'))
const popupsAmount = document.querySelectorAll('.playingcard').length
index = currentIndex + popupsAmount + 1
}
A GIF might be more clear:
How to always put a div on top of the "pile" when dragging it, no matter how many I have?
The problem looks like you are only incrementing the z-index. If you click on one card multiple times you would have to click on the other cards the same number of times to catch them up.
In a three card example I would go the opposite direction and have the z-index start in one of 3 states for all cards 0 - 2. Initialize all three cards z-index with a unique number and when a card is clicked make it z-index = 2 and decrement the others if not 0.
Edit: The reason I attempted this was because I had a similar z-index problem at work myself and I had to do quite a bit of looking to figure out my issue. I am not clever enough to refine it anymore than this:
https://jsfiddle.net/damussel06/gkso24va/168/
const cardZero = document.getElementById("cardZero");
const cardOne = document.getElementById("cardOne");
const cardTwo = document.getElementById("cardTwo");
$(document).ready(function() {
// z-index initalize
cardZero.style.zIndex = 1;
cardZero.innerHTML = cardZero.style.zIndex;
cardOne.style.zIndex = 2;
cardOne.innerHTML = cardOne.style.zIndex;
cardTwo.style.zIndex = 3;
cardTwo.innerHTML = cardTwo.style.zIndex;
})
function cardOnTop(c) {
/* clicked card gets z=index of 3 */
topCard = document.getElementById(c);
/* if top card is already on top do nothing */
if (topCard.style.zIndex != 3) {
/* set cards */
switch (c) {
case 'cardZero':
// fix cards one and two
if (cardZero.style.zIndex == 1) {
cardOne.style.zIndex--;
cardTwo.style.zIndex--;
} else {
cardOne.style.zIndex = 1;
cardTwo.style.zIndex = 2;
}
// set picked cardZero
cardZero.style.zIndex = 3;
break;
case 'cardOne':
// fix cards zero and two
if (cardOne.style.zIndex == 1) {
cardZero.style.zIndex--;
cardTwo.style.zIndex--;
} else {
cardZero.style.zIndex = 1;
cardTwo.style.zIndex = 2;
}
// set picked cardOne
cardOne.style.zIndex = 3;
break;
case 'cardTwo':
// fix cards zero and one
if (cardTwo.style.zIndex == 1) {
cardZero.style.zIndex--;
cardOne.style.zIndex--;
} else {
cardZero.style.zIndex = 1;
cardOne.style.zIndex = 2;
}
// set picked cardTwo
cardTwo.style.zIndex = 3;
break;
default:
cardTable.innerHTML = 'error !';
}
/* displays new style.z-index on card */
cardZero.innerHTML = cardZero.style.zIndex;
cardOne.innerHTML = cardOne.style.zIndex;
cardTwo.innerHTML = cardTwo.style.zIndex;
}
}

javascript: clicking numerical boxes - increment not working

using the code below, I've created a grid of buttons, 5x5, with random 1-25 numbers assigned to each button. They are to be clicked in numerical order, each's background turns red when clicked in the correct order. I can't use a global variable for this prompt. Without a global variable, I can't figure out how to increment the correctNumbers function which checks whether the right number is clicked each time. I think I'm missing something, a js function or something that would enable an incrementing variable declared within the incrementing function. I'm not looking for the whole explanation, just tips on functions i might not know about, and whether or not what i'm trying to do just isn't logicly possible.
<div id="numbers" class="hidden"></div>
<div id="youWon" class="hidden">You Won!</div>
The relevant JS:
... /**
* Gives the numbers a random order
* the "Fisher-Yates shuffle" found at: https://www.frankmitchell.org/2015/01/fisher-yates/
* #param {*} array
*/
const shuffle = (array) => {
let i = 0,
j = 0,
temp = null
for (i = array.length - 1; i > 0; i -= 1) {
j = Math.floor(Math.random() * (i + 1))
temp = array[i]
array[i] = array[j]
array[j] = temp
}
}
/**
* Generates an array of numbers 1-25
*/
const generateNums = () => {
document.getElementById("youWon").classList.toggle("hidden", "visible");
const numberArray = [];
for (let a = 1; a <= 25; a++) {
numberArray.push(a);
}
shuffle(numberArray);
let numEl = document.getElementById('numbers'); //write into html div id "numbers"
for (let b = 0; b <= 24; b++) { //loop to create button array
let newBtn = document.createElement('button'); //create buttons
newBtn.className = 'number'; //assign newBtns 'number' class
newBtn.innerText = numberArray[b]; //assign numbers to each button
numEl.appendChild(newBtn); //match with number elements in "numbers" array
newBtn.addEventListener("click", onNumberClick) //create function trigger
}
}
/**
* Creates a function to decide correct and incorrect clicks
* When a user clicks a number, if it is the next number in order, then it turns a different color for the remainder of the test
* If it is the wrong number, nothing happens
* #param {*} event
*/
const incrementNum = (correctNumber) => {
correctNumber++;
}
const onNumberClick = (event) => {
let correctNumber = 1; //start at 1
let numberVal = event.target; //apply it to clicks on the numbers
if (Number(numberVal.innerHTML) + 1 == incrementNum(correctNumber)) {
incrementNum(correctNumber);
numberVal.classList.add("red");
}
if (correctNumber == 26) {
document.getElementById("youWon").classList.toggle("visible"); //show win message if 25 is the last button and gets clicked
}
}
I would suggest that you count the number of elements in the DOM that have the class "red" and add 1... checking if the innerHTML is equal to that number to get the sequence right. So, instead of this:
if (Number(numberVal.innerHTML) + 1 == incrementNum(correctNumber)) {
incrementNum(correctNumber);
numberVal.classList.add("red");
}
You can have something like this:
if(Number(numberVal.innerHTML) == document.getElementsByClassName('red').length + 1) {
numberVal.classList.add("red");
}

Why does my js cause the last image of the array to appear before navigating to the view?

As of now, my code successfully goes from one image to another upon clicking on it.
However, the issue I'm facing is that once you get to the last image and click (to redirect user to indexTwo.html), it takes the user back to the first image for like 2 seconds and then it redirects to indexTwo.html.
My question is: How would I prevent that behavior from happening? In other words: click through all the images and once the last image's clicked, redirect user to indexTwo.html without seeing the first image at all.
Note: I know if (imageId === 0) {...} statement is the culprit but not sure how to go about fixing it.
let theImage = document.getElementById('the-image');
let index = [
"http://tech21info.com/admin/wp-content/uploads/2013/03/chrome-logo-200x200.png",
"https://cdn.tutsplus.com/net/uploads/legacy/155_drupal/200x200.png",
"https://townandcountryremovals.com/wp-content/uploads/2013/10/firefox-logo-200x200.png"
];
let op = 1;
let imageId = 0;
let clickedImage = () => {
// start animation opacity
if(op === 1) {
let timer = setInterval(() => {
if(op <= 0.1) {
// load the next image
imageId = (1 + imageId) % index.length;
theImage.src = index[imageId];
// reset the opacity
theImage.style.opacity = op = 1;
clearInterval(timer);
if (imageId === 0) {
window.location = "indexTwo.html";
}
} else {
op -= 0.1;
theImage.style.opacity = op;
}
}, 50)
}
};
do you need to verify the datatypes match for imageId and the number 0? if not, try using == instead of ===

Categories

Resources