Counting number of images in folder (sort of) - javascript

What I am trying to do is an image gallery in which you can navigate by arrows (left and right). There is x images in the image folder (for example let it be 8). Clicking the left arrow triggers the left() function:
function left() {
number--; // number variable is 1 for default, it's used for changing images which are named slide1 slide2... so I can change it like that: "slide" + number
document.getElementById("js_gallery_image").setAttribute("src", "img/" + "slide" + number + ".jpg"); //changes the number
and the right arrrow triggers the right() function which is the same but with number++ instead of number--.
And this was the code that I made at first but there is one more thing: when you go to the last image (8th one) clicking the right button should show the first image. I did it by adding an invisible img tag named #test, so I changed the right() function to that:
function right() {
number++;
document.getElementById("test").setAttribute("src", "img/" + "slide" + number + ".jpg"); //changes the test image path to next slide.
document.getElementById("test").onload = () => change(); // if number isn't greater than 8 image will load and change() function will change the image in gallery
document.getElementById("test").onerror = () => {
number = 1;
change();
}; // if number is greater than 8 it will throw an error so number will be changed to one and the change() function will be triggered.
};
and that worked, but how to do it with the left function, so when the first image is displayed and you click the left arrow it will show you the last image? I tried to do that:
function left() {
number--;
document.getElementById("test").setAttribute("src", "img/" + "slide" + number + ".jpg");
document.getElementById("test").onload = () => change();
document.getElementById("test").onerror = () => {
let err = false;
do {
number++
document.getElementById("test").setAttribute("src", "img/" + "slide" + number + ".jpg");
document.getElementById("test").onerror = () => err = true;
} while (err == false) //it pluses 1 to number untill test image throws an error which should be thrown when the number is greater than 8
};
And this didn't work, it gives me a result of an infinite loop. I trieed to do that using break statements and others but nothing worked.

From what it looks like, you have an issue with the loop and the fact that it is adding a listener, but is not waiting for the listener to do anything, causing the code to just loop and not wait for anything, and changing it again, not wait...
the fix for that could be to just make another loop inside of a loop and set the number to a variable to reduce having to download the same assets multiple times.
The code would look like this:
let maxImg = 0;
function findMax(){
let currentMax = 1; //initially sets it to one because I presume you will have at least one image in the gallary
let stoploop = false;
let testImg = new Image();
testImg.onload = function () {
currentMax += 1;
stoploop = true;
}
testImg.onerror = function () {
maxImg = currentMax;
stoploop = true;
}
testImg.setAttribute("src", "img/" + "slide" + (currentMax + 1) + ".jpg");
function loop() {
setTimeout(function () {
if (!stoploop) {
loop();
} else {
if (maxImg < 1) {
testImg.setAttribute("src", "img/" + "slide" + (currentMax + 1) + ".jpg");
loop();
}
}
}, 500);//done to not completely bog down the computer, may adjust if needbe
}
loop();
}
so in practice, you would have to have your left function look like this:
function left() {
number--;
document.getElementById("test").setAttribute("src", "img/" + "slide" + number + ".jpg");
document.getElementById("test").onload = () => change();
document.getElementById("test").onerror = () => {
document.getElementById("test").setAttribute("src", "img/" + "slide" + maxImg + ".jpg");
number = maxImg;
};
}
NOTE: With your current solution of testing through the client side you will have to have increased load time because you have to load all of the images, and that includes waiting for the images to be downloaded from the server one by one.

Have a go with this
let number = 0;
let last = 9999999;
const img = document.getElementById("test")
const prev = document.getElementById("prev");
const next = document.getElementById("next");
const changeImg = () => img.setAttribute("src", "img/" + "slide" + number + ".jpg");
img.onerror = () => { // should only trigger once at the end of the list
last = number - 1;
if (number<0) {
console.log("no images found")
return;
}
number = last;
changeImg()
}
nav.addEventListener("click",function(e) {
tgt = e.target;
dir = tgt.id === "prev" ? -1 : 1;
number += dir
if (number < 0 || number >= last) number = 0;
prev.disabled=number===0;
changeImg()
})
<div id="nav">
<button class="btn" id="prev" disabled type="button">Prev</button>
<img id="test" src="img/slide0.jpg">
<button class="btn" id="next" type="button">Next</button>
</div>

See if this helps:
Working example: https://jsfiddle.net/diogocosta/5w042uL1/4/
HTML
<button id="prev-button" disabled onclick="prevImage()">Previous</button>
<button id="next-button" onclick="nextImage()">Next</button>
<img src="https://placeimg.com/300/300/any" id="img-content">
JS
const images = [
'https://placeimg.com/300/300/animals',
'https://placeimg.com/300/300/nature',
'https://placeimg.com/300/300/people',
'https://placeimg.com/300/300/tech',
'https://placeimg.com/300/300/sepia',
]
const TOTAL_IMGS = 5
let currentImg = 1
const imgContent = document.getElementById('img-content')
function nextImage () {
if (currentImg < TOTAL_IMGS) {
currentImg++
imgContent.setAttribute('src', images[currentImg-1])
}
updateButtonState()
}
function prevImage () {
if (currentImg > 1) {
currentImg--
imgContent.setAttribute('src', images[currentImg-1])
}
updateButtonState()
}
// Enables and disables button
function updateButtonState() {
const nextButton = document.getElementById('next-button')
const prevButton = document.getElementById('prev-button')
if (currentImg === 1) {
nextButton.removeAttribute('disabled')
prevButton.setAttribute('disabled', true)
} else if (currentImg === TOTAL_IMGS) {
nextButton.setAttribute('disabled', true)
prevButton.removeAttribute('disabled')
} else {
nextButton.removeAttribute('disabled')
prevButton.removeAttribute('disabled')
}
}
Cheers,

Related

JS random order showing divs delay issue

I got function within JS which is supposed to show random order divs on btn click.
However once the btn is clicked user got to wait for initial 10 seconds ( which is set by: setInterval(showQuotes, 10000) ) for divs to start showing in random order which is not ideal for me.
JS:
var todo = null;
var div_number;
var used_numbers;
function showrandomdivsevery10seconds() {
div_number = 1;
used_numbers = new Array();
if (todo == null) {
todo = setInterval(showQuotes, 10000);
$('#stop-showing-divs').css("display", "block");
}
}
function showQuotes() {
used_numbers.splice(0, used_numbers.length);
$('.container').hide();
for (var inc = 0; inc < div_number; inc++) {
var random = get_random_number();
$('.container:eq(' + random + ')').show();
}
$('.container').delay(9500).fadeOut(2000);
}
function get_random_number() {
var number = randomFromTo(0, 100);
if ($.inArray(number, used_numbers) != -1) {
return get_random_number();
} else {
used_numbers.push(number);
return number;
}
}
function randomFromTo(from, to) {
return Math.floor(Math.random() * (to - from + 1) + from);
}
Question: How to alter the code so upon the btn click divs will start showing right away without initial waiting for 10 seconds? (take in mind I want to keep any further delay of 10 seconds in between of each div being shown)
Thank you.
Call it when you begin the interval
todo = setInterval((showQuotes(),showQuotes), 10000);

Why's my image fade behaving weirdly when fading into next & last image in the array?

I don't understand why the previous image's being shown for a split second before moving on to the next image upon clicking.
Also, when I reach the last image, it should redirect to indexTwo.html which it does successfully. However, 'image down' coming from the alt attribute in <img/> is being shown for like two seconds before it reaches indexTwo.html successfully.
I tried many many different attempts to rectify this, way too many to list!
How can I prevent this behavior from happening?
Here's my html:
<img id="the-image" class="img-responsive"
src="http://tech21info.com/admin/wp-content/uploads/2013/03/chrome-logo-200x200.png"
alt="image down"
onclick="clickedImage()"
/>
Here's my js:
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 = imageId + 1;
console.log(imageId);
theImage.src = index[imageId];
if (imageId >= index.length) {
window.location = "indexTwo.html";
}
// reset the opacity
theImage.style.opacity = op = 1;
clearInterval(timer);
} else {
op -= 0.1;
theImage.style.opacity = op;
}
}, 100);
}
};
The issue is that you are overflowing the array.
When imageId == 3 then the following line:
theImage.src = index[imageId];
results in theImage.src being set to undefined.
An easy way to resolve this would be to rearrange your code so that the image only advances to the next image if the id is within the bounds of the array.
let clickedImage = () => {
// start animation opacity
if(op === 1) {
let timer = setInterval(() => {
if(op <= 0.1) {
// load the next image
imageId = imageId + 1;
console.log(imageId);
if (imageId >= index.length) {
window.location = "indexTwo.html";
} else {
theImage.src = index[imageId];
// reset the opacity
theImage.style.opacity = op = 1;
}
clearInterval(timer);
} else {
op -= 0.1;
theImage.style.opacity = op;
}
}, 100);
}
};

Javascript counter++ skips counting

I am building a simple JS game but ++ keeps on adding up for no reason.
Here is the code:
var cities = ["Atlanta","Chicago","Honolulu","Houston","Nashville","Orlando","Philadelphia","Phoenix","Portland","Seattle"],
c = Math.floor((Math.random() * 10)),
city = cities[c].toUpperCase(),
cityArr = city.split(""),
length = city.length,
guess = 0,
$prompt = $('#prompt'),
x, //letter guess
i;
function randomCity() {
var $showCity = document.getElementById("showCity"), //ul
newLi,
letter;//each letter of city
for(i=0; i<cityArr.length; i++){
newLi = document.createElement("li");
$showCity.appendChild(newLi);
letter = document.createTextNode(cityArr[i]);
newLi.appendChild(letter);
}
$("#showCity li").css("color", "#fff");
}//end randomCity()
function play() {
if(guess == 6){
$("#alphabet").css("visibility", "hidden");
ending();
} else {
$prompt.fadeIn("slow").text("Guess a letter: ");
guessLetter();
}
} // end play function
function guessLetter() {
var showLetter;
guess++
console.log(guess); //SHOWS THE COUNTER ADDING UP CONTINUOUSLY AFTER 2
$("#alphabet li").on('click', function () {
$(this).css("visibility", "hidden");
x = this.id;
if (city.indexOf(x) == -1) {
$prompt.fadeIn("slow").text("No letter " + x);
setTimeout(play, 1500);
} else {
for (i = 0; i < length; i++) {
if (city[i] == x) {
$prompt.fadeIn("slow").text("There is letter " + x + "!");
showLetter = "#showCity li:nth-child("+(i+1)+")";
$(showLetter).css("color", "#0F9ED8");
}
} //for loop
setTimeout(play, 1500);
} //else
});
}
function ending(){ //STILL IN PROGRESS
var guessWord,
finalOutput;
$prompt.fadeIn("slow").text("What is the word? ");
//guessWord = word from input
finalOutput = (guessWord == city) ? "That is correct!" : "Sorry, that is wrong.";
$prompt.fadeIn("slow").text(finalOutput);
}
$(document).ready(function(){
$("#start").on('click', function() {
$(this).hide();
randomCity();
console.log(city);
$("#alphabet").css("visibility", "visible");
play();
});
}); // end ready
variable guess (the counter) has value of 4 after clicking the 2nd element, and has a value of 6 after clicking the 3rd element. I moved the var guess in different parts of my code but it is still doing that. This is very weird!
By doing
$("#alphabet li").on('click', function () { /* ... */}`
you're binding a new click handler every time the guessLetter() function gets executed. Multiple click handlers will call the play() function which in turn calls the guessLetter() function again, resulting in guess being incremented multiple times.
Bind the click handler only once.
You are attaching a new click handler to your list items every time the guessLetter function is called.
To fix it, you could move everything in guessLetter which occurs after your console.log call into the $(document).ready callback function.

Changing image to another on click

We are currently creating an image inside a holder (ch-item) as shown below:
<div class="ch-item ch-img-1" style="background: url(<?php echo get_template_directory_uri(); ?>/images/image1.jpg);">
If possible we would like to make this image change to another when clicked, and repeat this process six times in all, so each time it is clicked it changes to another image.
We would greatly appreciate any help. Thank you!
This is one way to do it using solely Javascript:
// Counter to keep track of which the current image is
var counter = 0;
// List of images
var images = [
'http://cdn.cutestpaw.com/wp-content/uploads/2011/11/Seemly-l.jpg',
'http://cdn.cutestpaw.com/wp-content/uploads/2011/11/Handsome-l.jpg',
// Add more images here
];
window.onload = function () {
// Get the container div
var gallery = document.getElementById('gallery');
// Run updateImage function on click
gallery.addEventListener('click', updateImage);
// Run updateImage on start
updateImage();
}
function updateImage() {
// Get the container div
var gallery = document.getElementById('gallery');
// Set background image
gallery.style.backgroundImage = 'url(' + images[counter] + ')';
// Update counter
counter++;
// Remove old class name
if (counter == 1) { // Remove last
gallery.className = gallery.className.replace(
' ch-img-' + images.length,
''
);
} else { // Remove previous
gallery.className = gallery.className.replace(
' ch-img-' + (counter - 1),
''
);
}
// Add new class name
gallery.className = gallery.className + ' ch-img-' + (counter);
// Reset counter when at the end of the images list
if (counter == images.length) {
counter = 0;
}
}
And here is a JSFiddle to try it out:
https://jsfiddle.net/0tg6up0o/14/

Sequentially highlighting divs using javascript

I'm trying to create kind of runway of lights and here's what it looks like now
http://jsfiddle.net/7NQvq/
var divs = document.querySelectorAll('div');
var index = 0;
setInterval(function(){
if(index > divs.length+20){
index = 0;
}
if(divs[index-1]){
divs[index-1].className = '';
}
if(divs[index]){
divs[index].className = 'active';
}
index++;
}, 50);
What I don't like about it is that it's completely inflexible and hard to adjust. Furthermore it also runs additional 20 empty cycles which is wrong. Is there a better way to achieve it (preferrably pure JS)?
It seemes that there must be some combination of setInterval and setTimeout but I just can't make it work.
I've made some adjustments to use a CSS animation rather than messing around with transitions and class toggling.
Updated Fiddle
All the JavaScript does now is define the animation delay for each dot.
You can adjust:
The animation delay - I just have i/10, but you could make it i/5, i/20... experiment!
The animation duration - it's set to 1s in my Fiddle, but try shorter and longer to see what happens
The 50% that indicates when the light has faded out
How about
function cycle(selector, cssClass, interval) {
var elems = document.querySelectorAll(selector),
prev = elems[0],
index = 0,
cssClassRe = new RegExp("\\s*\\b" + cssClass + "\\b");
if (elems.length === 0) return;
return setInterval(function () {
if (prev) prev.className = prev.className.replace(cssClassRe, "");
index %= elems.length;
elems[index].className += " " + cssClass;
prev = elems[index++];
}, interval);
}
and
var runwayIntval = cycle("div", "active", 100);
and at some point
clearInterval(runwayIntval);
See: http://jsfiddle.net/arNY8/1/
Of course you could argue that toggling a CSS class is a little limited. You could work with two callback functions instead: one to switch on a freely definable effect, one to switch it off:
function cycle(elems, enable, disable, interval) {
var prev = elems[0], index = 0;
if (elems.length === 0) return;
return setInterval(function () {
index %= elems.length;
if (prev) disable.call(prev);
enable.call(elems[index]);
prev = elems[index++];
}, interval);
}
and
var cycleIntval = cycle(
document.querySelectorAll("div"),
function () {
this.className += " active";
},
function () {
this.className = this.className.replace(/\s*\bactive\b/, "");
},
100
);

Categories