addEventListener only works once - javascript

I have an event listener. Inside it I'm looping through 3 images and applying style translateX(350px) to them. It works perfectly fine but it only works once.
I click right once and the images move to the right but if I click right again the images do not move. How do I make the Event Listener fire multiple times so images move multiple times? There is a similar question on Stack Overflow but I could not understand.
HTML:
<div id='container'>
<div class='image-container'>
<img class='move' src='images/news1.jpg'>
<img class='move' src='images/news2.jpg'>
<img class='move' src='images/news3.jpg'>
</div>
</div>
<a href="#" id='arrow-left'>left</a>
<a href="#" id='arrow-right'>right</a>
JavaScript:
let imageContainer = document.querySelector('.image-container');
let arrowLeft = document.getElementById('arrow-left');
let arrowRight = document.getElementById('arrow-right');
let images = document.getElementsByClassName('move');
arrowRight.addEventListener('click', function() {
for (i = 0; i < images.length; i++) {
images[i].style.transition = "0.5s";
images[i].style.transform = 'translateX(350px)';
}
});

You may need to use an accumulator for your transformations so that they remember their previous translation state, e.g, 350 the first time, 700 the 2nd, ... etc.:
try this:
var accum = 350;
let imageContainer = document.querySelector('.image-container');
let arrowLeft = document.getElementById('arrow-left');
let arrowRight = document.getElementById('arrow-right');
let images = document.getElementsByClassName('move');
arrowRight.addEventListener('click' , function() {
for (let i = 0 ; i < images.length; i++) {
images[i].style.transition = "0.5s";
images[i].style.transform = 'translateX(' + accum + 'px)';
}
accum+= 350;
});
<div id='container'>
<div class='image-container'>
<img class='move' src='images/news1.jpg'>
<img class='move' src='images/news2.jpg'>
<img class='move' src='images/news3.jpg'>
</div>
</div>
<a href="#" id='arrow-left'>left</a>
<a href="#" id='arrow-right'>right</a>

DEMO: http://jsfiddle.net/crmqj91t/15/
TranslateX takes the reference of the initial render position. So everytime you set translatex by 350px it remains at the same point with respect to its origin.
I've added a step counter, which sets the correct distance from the origin on each step.
let imageContainer = document.querySelector('.image-container');
let arrowLeft = document.getElementById('arrow-left');
let arrowRight = document.getElementById('arrow-right');
let images = document.getElementsByClassName('move');
let step = 1;
arrowRight.addEventListener('click', function() {
for (i = 0; i < images.length; i++) {
images[i].style.transition = "0.5s";
images[i].style.transform = 'translateX(' + step * 50 + 'px)';
}
step++;
});

Related

Click event on a slider goes at the end of the array

I am trying to create a simple slider using javascript.
So far, here is my code :
html:
<div class="carousel-container">
<div class="slides-container">
<div class="slide">
<img src="assets/images/image-1.jpg" alt="">
</div>
<div class="slide">
<img src="assets/images/image-3.jpg" alt="">
</div>
<div class="slide">
<img src="assets/images/image-2.jpg" alt="">
</div>
<div class="slide">
<img src="assets/images/image-3.jpg" alt="">
</div>
</div>
</div>
<div class="carousel-nav">
<button class="button-previous">Previous</button>
<button class="button-next">Next</button>
</div>
Js :
const slides = document.querySelectorAll('.slide')
const slidesContainer = document.querySelector('.slides-container')
const slidesCount = slides.length
const slideWidth = slides[0].clientWidth
const prevButton = document.querySelector('.button-previous')
const nextButton = document.querySelector('.button-next')
function nextSlide(slide) {
for (slide = 0; slide < slidesCount; slide++) {
slidesContainer.style.transform = 'translateX(-' + (slide * slideWidth) + 'px)'
}
}
nextButton.addEventListener('click', function() {
nextSlide()
})
The issue I have is that when I click on the next button, the slider goes all the way at the end of my array, thus skipping all the images in between.
What would be the best way to have my for loop stop at each image and go again when the button is clicked?
The reason it is skipping to the end with each click is: the for() loop. You are cycling through all of the slides — generating a new translateX coordinate with each pass – on every click. Leaving you with the last value every time.
The answer CheapGamer gave defines a pattern to avoid the for() loop trap by keeping track of the current “slide” index outside of the nextSlide() handler.
let current = 0; // <- independent index reference
function nextSlide(slide) {
if (current < slidesCount) {
slidesContainer.style.transform = 'translateX(-' + (current * slideWidth) + 'px)';
current++; // <- increment the index
}
}
// I changed the `slide` reference in `(slide * slideWidth)` to `current` to reflect the intent of the code
This takes care of incrementing you through the carousel until current equals 3. (The slide argument in nextSlide(slide) isn’t necessary.)
let current = 0;
function nextSlide(slide) {
if (current < slidesCount) {
slidesContainer.style.transform = 'translateX(-' + (slide * slideWidth) + 'px)';
current++;
}
}
I might not exactly understand what you try to do or why you intend to use a for loop but if it's just about selecting the next slide you may go with this:
var currentSlide = 0;
function nextSlide() {
//make sure currentSlide is in range
currentSlide = (currentSlide >= slidesCount) ? 0 : ++currentSlide;
slidesContainer.style.transform = 'translateX(-' + (currentSlide * slideWidth) + 'px)';
}
nextButton.addEventListener('click', nextSlide);
or a little bit more flexible:
var currentSlide = 0;
function nextSlide(direction /* Number */) {
if(!direction) direction = 1; //default
setSlide(currentSlide + direction % slidesCount);
}
function setSlide(index) {
if(index === currentSlide) return;
//make sure index is in range
while(index >= slidesCount) index -= slidesCount;
while(index < 0) index += slidesCount;
slidesContainer.style.transform = 'translateX(-' + (index * slideWidth) + 'px)';
currentSlide = index;
}
nextButton.addEventListener('click', function() {
nextSlide(this.dataset["direction"]); //by using data you don't need two different button classes
});
But if you insist on using a loop I'm afraid to tell that it's inappropriate in this case since the calculation of translateX is based on a given value.

How to delete elements created by CreateElement and AppendChild

[
// Calendar
var calendar = document.getElementById("calendar");
calendar.addEventListener("click",function(){
factbox.style.bottom = "70px";
factbox.style.transition = "0s";
calendar.style.transform = "rotateX(-50deg)";
calendar.style.transformOrigin = "bottom center";
var hotdogs = document.createElement("IMG");
hotdogs.src = "SVG/hotdog2.svg";
hotdogs.setAttribute = ("id", "hotdogs");
everywhere.parentNode.removeChild("hotdogs");
});
// Hotdog
var hotdog = document.getElementById("hotdog");
var everywhere = document.getElementById("everywhere");
var NumClick4 = 0;
hotdog.addEventListener("click",function(){
var hotdogs = document.createElement("IMG");
hotdogs.src = "SVG/hotdog2.svg";
hotdogs.setAttribute = ("id", "hotdogs");
everywhere.appendChild(hotdogs);
var x = Math.floor(( Math.random() * 450) + 800);
NumClick4++;
console.log(x + " " + NumClick4);
hotdogs.style.width = "200px";
hotdogs.style.left = x + "px";
if(NumClick4 == 1) {
factbox.style.bottom = "70px";
factbox.style.transition = "0s";
} else {
}
});
<div id="hotdog" class="">
<img src="SVG/hotdog2.svg" alt="hotdog">
</div>
<div id="everywhere"></div>
<div id="calendar">
<img src="SVG/calendar.svg" alt="calendar">
</div>
<div id="factbox">
</div>
enter image description here
Sorry if my code looks messy. So as shown on the image, I want the hotdogs on the sky, which were created by "CreateElement" and "AppendChild" method to disappear using "RemoveChild" method. For example, when I click other elements like "flag", I want the just the new "hotdogs" to disappear. I think I am not getting how it works. I would appreciate advice and tips.
Following line is wrong by 2 counts
everywhere.parentNode.removeChild("hotdogs");
You are trying to remove something which hasn't been added yet
You need to remove the Element rather than string
i.e.
everywhere.parentNode.removeChild(hotdogs);

Slider Images working correctly for right cursor but not left

I have created a website for a friend which includes a slideshow with his most recent Instagram posts. The slideshow has a right and left arrow cursor.
The right cursor is working how I want it to; it shows each image from the array upon each click, and keeps going regardless of how many times the user clicks the right cursor. When it reaches the last image in the array, it starts at the first image again due to an if statement put in place.
However, I am having trouble with the left cursor and I suspect it is the counter variable? With the current code, what happens is when I click the left cursor (with the page refreshed and without even clicking on the right cursor), it goes to the second image in the array index1, rather than the last. Then I click the left cursor again without any change happening, then when I click it the third time it goes to the last image in the array and works as it should until it reaches the second image in the array again - because after that it does not go to the first image, it skips the first image after another click of nothing happening and then goes to the last image in the array. Repeats itself in that manner (sorry for going so in-detail, but I want people to get an idea of whats happening here if the code doesn't help).
var sliderImages = [];
var counter = 1;
sliderImages[0] = "images/i1.png";
sliderImages[1] = "images/i2.png";
sliderImages[2] = "images/i3.png";
sliderImages[3] = "images/i4.png";
sliderImages[4] = "images/i5.png";
$("#right-arrow").click(function() {
$(".active").attr("src", sliderImages[counter]);
counter++;
$('#count').text(counter);
if (counter >= sliderImages.length) {
counter = 0;
}
});
$("#left-arrow").click(function() {
$(".active").attr("src", sliderImages[counter]);
counter--;
if (counter <= 0) {
counter = sliderImages.length
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section id="instagram-feed">
<div class="container">
<h2>INSTAGRAM GALLERY</h2>
<div class="insta-gallery">
<img src="images/left-arrow.png" class="arrow" id="left-arrow">
<img src="images/i1.png" class="active">
<img src="images/right-arrow.png" class="arrow" id="right-arrow">
</div>
</div>
<div class="clear"></div>
</section>
When your page loads, you set counter to 1. When you click the left-arrow button, the first thing you do is set the image to the counter value, and so you set the image to 1. (Remember, arrays are 0-indexed, meaning 1 refers to the second item).
You'd be better off if counter was referring to the current image, not trying to assume the upcoming one. See my comments in the modified code below for more information.
var sliderImages = [];
var counter = 0; //Start with first image instead of second
sliderImages[0] = "images/i1.png";
sliderImages[1] = "images/i2.png";
sliderImages[2] = "images/i3.png";
sliderImages[3] = "images/i4.png";
sliderImages[4] = "images/i5.png";
$("#right-arrow").click(function() {
counter++; //Moving forward by one
if (counter > sliderImages.length-1) counter = 0; //Wrap-around if we exceed length-1
$(".active").attr("src", sliderImages[counter]); //Update img src
});
$("#left-arrow").click(function() {
counter--; //Moving backward by one
if (counter < 0) counter = sliderImages.length-1; //Wrap-around if negative
$(".active").attr("src", sliderImages[counter]); //Update img src
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section id="instagram-feed">
<div class="container">
<h2>INSTAGRAM GALLERY</h2>
<div class="insta-gallery">
<img src="images/left-arrow.png" class="arrow" id="left-arrow">
<img src="http://via.placeholder.com/350x150" class="active">
<img src="images/right-arrow.png" class="arrow" id="right-arrow">
</div>
</div>
<div class="clear"></div>
</section>
Use a counter as if you're handling indexes, so set it initially at 0.
Here's the basic logic:
var sliderImages = [
"//placehold.it/100x100/0bf?text=O",
"//placehold.it/100x100/f0b?text=1",
"//placehold.it/100x100/0fb?text=2",
"//placehold.it/100x100/fb0?text=3",
"//placehold.it/100x100/b0f?text=4"
];
var n = sliderImages.length; // Total slides
var c = 0; // Counter
function anim () {
$("#mainImage").attr("src", sliderImages[c]);
}
$("#prev").on("click", function() {
--c; // Pre-decrement
if ( c < 0 ) { c = n-1; } // If lower than 0 - go to last slide
anim(); // animate
});
$("#next").on("click", function() {
++c; // Pre-increment
if ( c > n-1 ) { c = 0; } // If greater than num of slides - go to first slide
anim(); // animate
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<img id="mainImage" src="//placehold.it/100x100/0bf?text=O">
<br>
<button id="prev">←</button>
<button id="next">→</button>
<span id="count"></span>
Or here's a neater extracted formula of the above:
var sliderImages = [
"//placehold.it/100x100/0bf?text=O",
"//placehold.it/100x100/f0b?text=1",
"//placehold.it/100x100/0fb?text=2",
"//placehold.it/100x100/fb0?text=3",
"//placehold.it/100x100/b0f?text=4"
];
var n = sliderImages.length; // Total slides
var c = 0; // Counter
function anim () {
c = c<0 ? n-1 : c%n; // Fix counter
$("#mainImage").attr("src", sliderImages[c]); // Animate (or whatever)
}
$("#prev, #next").on("click", function() {
c = this.id==="next" ? ++c : --c; // Increement or decrement
anim(); // Animate
});
// If you have bullets than you can simply do like:
$(".bullet").on("click", function() {
c = $(this).index();
anim();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<img id="mainImage" src="//placehold.it/100x100/0bf?text=O">
<br>
<button id="prev">←</button>
<button id="next">→</button>
<span id="count"></span>
Updated your code a bit :
var sliderImages = [];
var counter = 0;
sliderImages[0] = "images/i1.png";
sliderImages[1] = "images/i2.png";
sliderImages[2] = "images/i3.png";
sliderImages[3] = "images/i4.png";
sliderImages[4] = "images/i5.png";
$("#right-arrow").click(function() {
counter++;
$(".active").attr("src", sliderImages[counter]);
$('#count').text(counter);
if (counter >= sliderImages.length) {
counter = 0;
}
});
$("#left-arrow").click(function() {
counter--;
$(".active").attr("src", sliderImages[counter]);
if (counter <= 0) {
counter = sliderImages.length - 1;
}
});

jQuery swap Next/Previous image, images in array

I have a literal array of image IDs and I need to swap them in <img src=""> to Next or Previous image on buttons click events. The initial current image ID is known from the img src provided server-side on initial page load.
Obviously, before swapping, the URL needs to be constructed with the target ID like this:
'http://site.com/images/' + imageID + '.jpg'
I'm a JS/jQuery beginner and would like to learn a correct, minimalistic approach. TIA.
My code to start off:
var images=["777777","666666","555555"];
var max = $(images).length;
$('#swapnextimg').click{
$("#imageswap").attr('src', ...... );
}
<a id="swapnextimg"></a>
<a id="swapprevsimg"></a>
<div id="imagebox">
<img id="imageswap" src="http://site.com/images/123456.jpg">
</div>
Here is an example:
http://jsfiddle.net/ndFGL/
$(function() {
// Image ID array
var images = ['240', '260', '250', '123', '200'];
var max = images.length;
// Get current image src
var curSrc = $('#imageswap').attr('src');
// Find ID in image src
var curID = curSrc.replace(/.*\/(.*?)\.jpg/i, '$1');
var curIdx = 0;
// Search image list for index of current ID
while (curIdx < max) {
if (images[curIdx] == curID) {
break;
}
curIdx++;
}
// For convenience
var imgSrcBase = 'http://placehold.it/';
// Next image on button (and image) click
$('#swapnextimg,#imageswap').click( function() {
curIdx = (curIdx+1) % max;
$("#imageswap").attr('src', imgSrcBase+images[curIdx]+'.jpg');
});
// Prev image on button click
$('#swapprevsimg').click( function() {
curIdx = (curIdx+max-1) % max;
$("#imageswap").attr('src', imgSrcBase+images[curIdx]+'.jpg');
});
});
Try below code,
var images=["777777","666666","555555"];
var max = $(images).length;
var imgPtr = 0;
$('#swapnextimg').click{
if (imgPtr == max) {
//comment the below return and set imgPtr to 0 for rotating the images
return;
}
$("#imageswap").attr('src', 'http://site.com/images/' + images[imgPtr++] + '.jpg');
}
$('#swapprevsimg').click{
if (imgPtr == 0) {
//comment the below return and set imgPtr to max-1 for rotating the images
return;
}
$("#imageswap").attr('src', 'http://site.com/images/' + images[imgPtr--] + '.jpg');
}
<a id="swapnextimg"></a>
<a id="swapprevsimg"></a>
<div id="imagebox">
<img id="imageswap" src="http://site.com/images/123456.jpg">
</div>
I assume that you've got one img tag, and only one. And that the purpose of this is to change that single image tag.
To do that you will need the nex and prev button.
<div id="container">
<img src="#" data-num="" />
<span class"prev">prev</span>
<span class"next">next</span>
</div>
The object:
var obj = [0: 'dog', 1: 'cat'];
now for the next and prev to work. We are going to take a look at the .on() method.
$('.container span').on('click', function(){
var $this = $(this), // span
currentNum = $this.siblings('img').data('num'), // img data-num
nextNum = $this.is('.next') ? currentNum+1 : currentNum-1, // if class is next add 1, if not remove 1
link = "http://site.com/images/" + obj[nextNum] + ".jpg" // the link
// either it's not the last images or it returns
nextNum != obj.length() || return
$('img').attr('src', link).data('num', nextNum)
})
Hope this helped you. If not please let me know

Cycling between 3 images (mobile Safari)

I have the following javascript. It works well when I am cycling between 2 images, but when I add a third it does not work correctly.
Here is my CSS:
img {
-webkit-transition-property: opacity;
-webkit-transition-duration: 2s;
position: absolute;
width: 320px;
height: auto;
}
img.fade-out {
opacity: 0;
}
img.fade-in {
opacity: 1;
}
Here is my javascript, which seems to work but seems laggy and definately not an elegant solution.
</head><body style="color: black">
<img id="one" class="fade-out" src="Wallpaper.png"/>
<img id="two" class="fade-out" src="Wallpaper0.png"/>
<img id="three" class="fade-out" src="Wallpaper1.png"/>
<script>
var images = ['Wallpaper.png', 'Wallpaper0.png', 'Wallpaper1.png'];
var index = 0;
var fade_in = one;
var fade_out = two;
var fade_foo = three;
fade_in.src = images[0];
fade_out.src = images[images.length - 1];
var fade = function () {
fade_in.src = images[index];
index = (index + 1) % images.length;
fade_in.className = 'fade-out';
fade_out.className = 'fade-in';
fade_foo.className = 'fade-out';
var fade_tmp = fade_in;
fade_in = fade_out;
fade_out = fade_foo;
fade_foo = fade_tmp;
setTimeout(fade, 15000);
};
fade();
</body></html>
For one thing, you're not changing fade_out.src. Try something like this:
fade_in.src = images[0];
fade_out.src = images[1]; // let's use image next to current for fade-out
var fade = function () {
fade_in.src = images[index];
index = (index + 1) % images.length;
fade_out.src = images[index]; // put next to current image into fade-out
// Code below does something misterious.
// You first switch classes between two img's, then switch variables themselves
// Why?
//fade_in.className = 'fade-out';
//fade_out.className = 'fade-in';
//var fade_tmp = fade_in;
//fade_in = fade_out;
//fade_out = fade_tmp;
setTimeout(fade, 15000);
};
Can't tell more since I don't know what exactly you're doing.
It seems you're only displaying one image at a time, so you don't need two variables, one will do. You just need to fade out the current image and bring in a new image:
var index = -1, count = /* total number of images */;
var image = null;
function fade() {
if (image != null)
image.className = 'fade-out';
index = (index + 1) % count;
image = document.getElementById('image-' + index);
image.className = 'fade-in';
setTimeout(fade, 15000);
}
fade();
This assumes that you have set up all the images in HTML as follows:
<img id="image-0" class="fade-out" src="..." />
<img id="image-1" class="fade-out" src="..." />
<img id="image-2" class="fade-out" src="..." />
...
Note that you can achieve cross-fading only if you have several images preloaded, as in the above example. If you use only one image and change the source, the previous image will be lost when you try to fade in the new one.
you're not waiting for you transitions to finish before you swap the source. we just need to rearrange the order of things.
var fade = function() {
fade_in.className = 'fade-out';
fade_out.className = 'fade-in';
setTimeout(function() {
index = (index + 1) % images.length;
fade_in.src = images[index]; // should be completely invisible at this time
var fade_tmp = fade_in;
fade_in = fade_out;
fade_out = fade_tmp;
}, 2000); // 2 seconds, same as your transition time
setTimeout(fade, 15000);
};
setTimeout(fade, 15000);
here the only work that the fade method does is to change the classes, which initiates the transitions. we set a delay that matches your transition time to update the index and swap the image source.
edits: i guess i'm not making it clear what's going on and the assumptions i'm making. here's my complete html except for the provided css which is the same. i also fixed an issue with image order since the last example.
<body>
<img id="one" class="fade-out" /><img id="two" class="fade-out" />
<script>
var images = ['16jog8h.jpg', '20_11_2007_0044537001195507712_joe_baran.jpg', '400davesrig.jpg'];
var index = 0;
var fade_in = document.getElementById('one');
var fade_out = document.getElementById('two');
// fade_in.src = images[0];
fade_out.src = images[0];
var fade = function() {
fade_in.className = 'fade-out';
fade_out.className = 'fade-in';
setTimeout(function() {
index = (index + 1) % images.length;
fade_in.src = images[index];
var fade_tmp = fade_in;
fade_in = fade_out;
fade_out = fade_tmp;
}, 2000);
setTimeout(fade, 5000);
};
fade();
</script>
</body>

Categories