multiple carousel maximum callstack exceeded on single page Jquery/Vanilla Javascript - javascript

So I'm trying to implement an infinite autoplay multiple carousel using Jquery.
I have created two different carousel blocks on same page
this is the body of the page
<div class="rotating_block">
<div class="block one"></div>
<div class="block two"></div>
<div class="block three"></div>
</div>
<br>
<div class="rotating_block">
<div class="block one"></div>
<div class="block two"></div>
<div class="block three"></div>
</div>
CSS for the same
.rotating_block {
display: flex;
}
.one {
background: red;
height: 100px;
width: 100px;
}
.two {
background: green;
height: 100px;
width: 100px;
}
.three {
background: blue;
height: 100px;
width: 100px;
}
In JS i've created slideIndex array and childrens (blocks) array for each carousel
slideIndex[n] stores current slide number to show while
childrens[n] stores array of blocks/childrens
For each parent div '.rotating_block' I'm storing its slideIndex and its children in those arrays and performing the carousel function.
At the end I'm calling for setTimeout so as to run the function once again every five seconds and change the slide to give carousel like effect the problem is I'm getting max call exceeded stack / console getting logged every second multiple times
var slideIndex = [];
var childrens = [];
$(".rotating_block").each(function (index) {
slideIndex[index] = 0;
childrens[index] = $(this).find(".block");
function carousel(children, slideIndex) {
console.log('hello world');
for (let i = 0; i < children.length; i++) {
$(children[i]).hide();
}
if (slideIndex > children.length) {
slideIndex = 1;
}
$(children[slideIndex - 1]).show();
setTimeout(carousel(children, slideIndex), 5000);
}
carousel(childrens[index], slideIndex[index]);
});
link to pen

The problem is that you are passing the function carousel with parameters which is actually calling the function.
setTimeout(carousel(children, slideIndex), 5000);
To solve this, pass an arrow function to setTimeout
setTimeout(() => carousel(children, slideIndex), 5000);
EDIT
Also, you forgot to increment the slideIndex value:
var slideIndex = [];
var childrens = [];
$(".rotating_block").each(function (index) {
slideIndex[index] = 0;
childrens[index] = $(this).find(".block");
function carousel(children, slideIndex) {
console.log(slideIndex);
for (let i = 0; i < children.length; i++) {
$(children[i]).hide();
}
//////////////////////
slideIndex++;
//////////////////////
if (slideIndex > children.length) {
slideIndex = 1;
}
$(children[slideIndex - 1]).show();
setTimeout(() => carousel(children, slideIndex), 5000);
}
carousel(childrens[index], slideIndex[index]);
});

Related

JS Image Slider with specific text to each image

I've followed a tutorial about JS image sliders. I'm trying to have a text box display on each image (figured that out) but I need the text to be specific for each image. The images being grabbed from an img folder and are in order (image-0, image-1, etc). I'm guessing I'll need some array but I can't figure out how to do this in JS and have the corresponding text display on each correct image. Code provided. Any help?
HTML
<body>
<div class="images">
<div id="btns">
<button type="button" class="btn prevBtn">↩</button>
<button type="button" class="btn nextBtn">↪</button>
</div>
<div id="textBlock">
<h4>This is the image</h4>
</div>
</div>
<script src="script.js"></script>
</body>
JS
const nextBtn = document.querySelector(".nextBtn");
const prevBtn = document.querySelector(".prevBtn");
const container = document.querySelector(".images");
let counter = 0;
nextBtn.addEventListener("click",nextSlide);
prevBtn.addEventListener("click",prevSlide);
function nextSlide () {
container.animate([{opacity:"0.1"},{opacity:"1.0"}],{duration:1000,fill:"forwards"});
if(counter === 4){
counter = -1;
}
counter++;
container.style.backgroundImage = `url(img/image-${counter}.jpg`
}
function prevSlide () {
container.animate([{opacity:"0.1"},{opacity:"1.0"}],{duration:1000,fill:"forwards"});
if(counter === 0){
counter = 5;
}
counter--;
container.style.backgroundImage = `url(img/image-${counter}.jpg`
}
Since you counter is indexed 0 and goes up to 𝑛 all you need is an array:
const descriptions = [
"A nice walk in the park", // for the image counter 0
"My dog and me", // for the image counter 1
// etc.
];
than all you need to do is:
textBlock.textContent = descriptions[counter];
But...
I don't know where you found that toturial but it's a really a great example on how not to build a gallery. The animation is odd, it's overly simplistic and cannot account for multiple galleries. It's repetitive and unmodular. And the total number of slides should never be hardcoded, that's why we use a programming language after all. And yes, it can count the number of items using .length.
Code should be reusable:
class Gallery {
constructor(id, slides) {
this.slides = slides || [];
this.total = this.slides.length;
this.curr = 0;
this.EL = document.querySelector(id);
this.EL_area = this.EL.querySelector(".Gallery-area");
this.EL_prev = this.EL.querySelector(".Gallery-prev");
this.EL_next = this.EL.querySelector(".Gallery-next");
this.EL_desc = this.EL.querySelector(".Gallery-desc");
const NewEL = (tag, prop) => Object.assign(document.createElement(tag), prop);
// Preload images
this.ELs_items = this.slides.reduce((DF, item) => (DF.push(NewEL("img", item)), DF), []);
this.EL_area.append(...this.ELs_items);
// Events
this.EL_prev.addEventListener("click", () => this.prev());
this.EL_next.addEventListener("click", () => this.next());
// Init
this.anim();
}
// Methods:
anim() {
this.curr = this.curr < 0 ? this.total - 1 : this.curr >= this.total ? 0 : this.curr;
this.ELs_items.forEach((EL, i) => EL.classList.toggle("is-active", i === this.curr));
this.EL_desc.textContent = this.slides[this.curr].alt;
}
prev() {
this.curr -= 1;
this.anim();
}
next() {
this.curr += 1;
this.anim();
}
}
// Use like:
new Gallery("#gallery-one", [
{alt: "My fluffy dog and me", src: "https://picsum.photos/400/300"},
{alt: "Here, we seem happy!", src: "https://picsum.photos/300/300"},
{alt: "We are making pizza?", src: "https://picsum.photos/600/300"},
]);
.Gallery {
position: relative;
height: 300px;
max-height: 100vh;
}
.Gallery-area > * {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
object-fit: cover;
transition: opacity 0.3s;
opacity: 0;
}
.Gallery-area > *.is-active {
opacity: 1;
}
.Gallery-btns {
position: absolute;
bottom: 20px;
width: 100%;
text-align: center;
}
.Gallery-desc {
position: absolute;
top: 20px;
width: 100%;
text-align: center;
font-size: 3em;
}
<div class="Gallery" id="gallery-one">
<div class="Gallery-area"></div>
<div class="Gallery-btns">
<button type="button" class="btn Gallery-prev">←</button>
<button type="button" class="btn Gallery-next">→</button>
</div>
<div class="Gallery-desc"></div>
</div>

Building a vanilla carousel - stuck on one peice of logic

Any mentorship or guidance would be most welcomed.
I am trying to make a vanilla JS carousel and I am so close to realising my objective to build one.
However; I cannot seem to get the prev or next buttons to move the carousel backwards or forwards. The buttons "work" they go up and down in value; they do not change the style. I can see that console logging the values.
I've tried passing the function back onto itself - however, I cannot think of a way of initialising the start frame; if that is the best way.
Adding the slideIndex value into the style rule doesn't work. What I get is if you keep on pressing "prev" for example; eventually, another frame randomly pops up below.
Any help would be very much welcomed.
On a side note - is there a better way to work with variable scoping; without everything requiring this?
'use strict';
function carousel(n) {
this.slideIndex = n;
this.slides = document.querySelectorAll('.homepage_carousel_wrapper .homepage_carousel');
[...this.slides].forEach(function(x) {
x.style.display = 'none';
});
this.slides[this.slideIndex-1].style.display = "flex";
this.prev = function(n) {
this.slideIndex += n;
if (this.slideIndex < 1) {
this.slideIndex = this.slides.length;
}
console.log(`${this.slideIndex}`);
this.slides[this.slideIndex].style.display = "flex";
}
this.next = function(n) {
this.slideIndex += n;
if (this.slideIndex > this.slides.length) {
this.slideIndex = 1;
}
console.log(`${this.slideIndex}`);
this.slides[this.slideIndex].style.display = "flex";
//carousel(this.slideIndex)
}
};
window.addEventListener('load', function() {
const hp_carousel = new carousel(3);
let carouselPrev = document.getElementById('carousel_prev');
carouselPrev.addEventListener('click', function(e){
hp_carousel.prev(-1);
e.preventDefault();
e.stopPropagation();
}, false);
let carouselNext = document.getElementById('carousel_next');
carouselNext.addEventListener('click', function(e){
hp_carousel.next(1);
e.preventDefault();
e.stopPropagation();
}, false);
});
.homepage_carousel:nth-child(1) {
background-color: red;
width: 100%;
height: 200px;
}
.homepage_carousel:nth-child(2) {
background-color: blue;
width: 100%;
height: 200px;
}
.homepage_carousel:nth-child(3) {
background-color: green;
width: 100%;
height: 200px;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>carousel</title>
</head>
<body>
<a id='carousel_prev'>prev</a>
<a id='carousel_next'>next</a>
<div class='homepage_carousel_wrapper'>
<div class='homepage_carousel'>
<h1>Frame 1</h1>
</div>
<div class='homepage_carousel'>
<h1>Frame 2</h1>
</div>
<div class='homepage_carousel'>
<h1>Frame 3</h1>
</div>
</div>
</body>
</html>
I have made some modifications to the HTML and CSS, and have rewritten most of the JavaScript.
Main Modifications
HTML
Changed the controls from links to buttons.
Moved the controls inside the carousel.
CSS
Removed repeated CSS.
JavaScript
Added spacing to make the code more readable.
Added a few comments to make the code easier to understand.
Modified the carousel constructor to allow multiple carousels to be made.
Moved the control event listeners inside the carousel constructor.
Replaced the prev() and next() functions with a changeSlide() function.
'use strict';
window.addEventListener('load', function() {
const hpCarousel = new carousel('homepage_carousel', 3);
});
function carousel(id, index) {
// Set slide index and get slides
this.slideIndex = index;
const carousel = document.getElementById(id);
this.slides = [...carousel.getElementsByClassName('slide')];
// Get controls and add event listeners
const prev = carousel.getElementsByClassName('prev')[0];
const next = carousel.getElementsByClassName('next')[0];
prev.addEventListener('click', () => {
this.changeSlide(-1);
});
next.addEventListener('click', () => {
this.changeSlide(1);
});
// Functions for managing slides
this.hideAll = function() {
this.slides.forEach(function(slide) {
slide.style.display = 'none';
});
}
this.show = function() {
this.hideAll();
this.slides[this.slideIndex - 1].style.display = 'flex';
}
this.changeSlide = function(amount) {
this.slideIndex += amount;
this.slideIndex = (this.slideIndex > this.slides.length) ? 1 :
(this.slideIndex < 1) ? this.slides.length : this.slideIndex;
this.show();
}
// Show the specified slide
this.show();
}
.slide {
width: 100%;
height: 200px;
}
.slide:nth-child(1) {
background-color: red;
}
.slide:nth-child(2) {
background-color: blue;
}
.slide:nth-child(3) {
background-color: green;
}
<div id='homepage_carousel'>
<button class='prev'>prev</button>
<button class='next'>next</button>
<div>
<div class='slide'>
<h1>Frame 1</h1>
</div>
<div class='slide'>
<h1>Frame 2</h1>
</div>
<div class='slide'>
<h1>Frame 3</h1>
</div>
</div>
</div>

How to create Image slideshow with slide left/right animation using javascript only?

I am working on simple solution which requires image slider.
Right now it is just fading in and out. i would like to add some slide animation
I can simply do it using jquery but i don't want to use any external libraries.
var counter = 0;
var slideImgs = [
"http://placesforkidsct.com/wp-content/uploads/2016/07/300x250.png",
"https://abbs.edu.in/wp-content/uploads/2018/01/self-development-300x140.jpg",
"https://abbs.edu.in/wp-content/uploads/2018/01/self-development-300x140.jpg"
]
var imgelm = document.getElementById("300x250_Image");
var inst = setInterval(change, 1500);
function change() {
// elem.innerHTML = text[counter];
imgelm.src = slideImgs[counter];
counter++;
if (counter >= slideImgs.length) {
counter = 0;
// clearInterval(inst); // uncomment this if you want to stop refreshing after one cycle
}
}
change();
<div class="300x250_section_3" style="float: left;margin-top: 13px;">
<div class="300x250_center_img" style="
width: 300px;
height:140px;
float: left;
">
<img src="http://placesforkidsct.com/wp-content/uploads/2016/07/300x250.png" style="width: 300px;text-align: center;display: block; margin:auto; max-height:140px;
" id="300x250_Image" class="slide_1">
</div>
</div>

JavaScript - periodically change "active" image

I have 4 pictures and want them to periodically change class (I have .active class, which is similar to hover).
.active,
.pic:hover{
position: absolute;
border: 1px solid black;
transform: scale(1.1);
transition: transform .2s;
}
Basically I need the first picture to have the class active and after some time change it so the next picture has the class and the first one lose it.
Is something like that even possible?
Picture in HTML:
<div class="products">
<a href="http://example.com/produkt1">
<img class="pic" src="image.jpg" alt="image" width="75" height="75">
</a>
</div>
and JS:
productIndex = 0;
slideshow();
function slideshow(){
var i;
var pic = document.getElementsByClassName("pic");
for(i = 0; i < pic.length; i++){
pic[i].className = pic[i].className.replace("active", "");
}
productIndex++;
if(productIndex > pic.length){
productIndex = 1;
}
pic[productIndex-1].className += active;
setInterval(slideshow, 2000);
}
You can use setInterval to run a function periodically that will change the active class. Something like this (psuedo-code):
var imageArray = [];
var activeIndex = 0;
setInterval(function(){
imageArray[activeIndex].removeClass('active');
activeIndex++;
activeIndex %= 4;
imageArray[activeIndex].addClass('active');
}, 5000);
The number value passed in as a parameter is how many milliseconds to wait before running the function again. In this example, 5 seconds will pass between the classes are changed.
setInterval Reference
This is ugly but it could work for super basic ... You just need to update the div blocks with images if necessary. Uses jquery...
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
<style>
div {
width:50px;
height:50px;
background-color: black;
margin-bottom:10px;
}
.active {
background-color: red;
}
</style>
</head>
<body>
<div id="pic1"></div>
<div id="pic2"></div>
<div id="pic3"></div>
<div id="pic4"></div>
<script>
let lastActive = 0;
setInterval(()=>{
$('div').removeClass('active');
if(lastActive === 0){
$('#pic1').addClass('active');
lastActive = 1;
}
else if(lastActive === 1){
$('#pic2').addClass('active');
lastActive = 2;
}
else if(lastActive === 2){
$('#pic3').addClass('active');
lastActive = 3;
}
else if(lastActive === 3){
$('#pic3').addClass('active');
lastActive = 4;
}
else if(lastActive === 4){
$('#pic1').addClass('active');
lastActive = 1;
}
}, 500)
</script>
</body>
</html>
Matt L. has a good point here. Your code has the setInterval inside your slideshow function, otherwise it's fine.
productIndex = 0;
slideshow();
function slideshow(){
var i;
var pic = document.getElementsByClassName("pic");
for(i = 0; i < pic.length; i++){
pic[i].className = pic[i].className.replace("active", "");
}
productIndex++;
if(productIndex > pic.length){
productIndex = 1;
}
pic[productIndex-1].className += active;
}
setInterval(slideshow, 2000);
could probably work. Matt's answer is a lot better, and I came up with something similar, which is testable on jsfiddle.
You could do it like this for example:
$(document).ready(function() {
setInterval(function() {
var active = $('.active');
active.nextOrFirst().addClass('active');
active.removeClass('active');
}, 3000);
});
$.fn.nextOrFirst = function(selector)
{
var next = this.next(selector);
return (next.length) ? next : this.prevAll(selector).last();
};
.active,
.pic:hover{
border: 1px solid black;
}
.pic {
width: 150px;
margin: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="image-container">
<img class="pic active" src="https://via.placeholder.com/350x150">
<img class="pic" src="https://via.placeholder.com/350x150">
<img class="pic" src="https://via.placeholder.com/350x150">
<img class="pic" src="https://via.placeholder.com/350x150">
</div>
Edit:
This, instead of most other solutions, will work with any amount of items. To use it only on pictures just specify via selector in the function.
Checkout this working example. I've made use of a combination of setInterval and setTimeout.
$(window).ready(()=>{
// get all the images inside the image-container div
let $images = $('.image-container').find('.image');
let currImage = 0;
// execute this code every 2 seconds
window.setInterval(()=>{
// add the active class to the current image
$($images[currImage]).addClass('active');
setTimeout(()=>{
// execute the code here after 1.5 seconds
// remove the active class from the previous image
$($images[currImage-1]).removeClass('active');
}, 1500);
// make sure we don't go over the number of elements in the collection
currImage = currImage >= $images.length ? 0 : currImage + 1;
}, 2000);
});
.image.active {
border: thin solid blue;
}
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<div class="image-container" class="">
<img src="https://via.placeholder.com/200x200" class="image active">
<img src="https://via.placeholder.com/200x200" class="image">
<img src="https://via.placeholder.com/200x200" class="image">
<img src="https://via.placeholder.com/200x200" class="image">
</div>
Do make sure that the code in setTimeout will execute before the next interval. Meaning, the time set for setTimeout is always less than setInterval's :)
Yes it is possible:
function carousel() {
var images = document.querySelectorAll(".container img");
for(var i = 0; i < images.length; i++) {
if(images[i].classList.contains("active")) {
images[i].classList.remove("active");
if(i == images.length - 1) {
images[0].classList.add("active");
} else {
images[i + 1].classList.add("active");
}
break;
}
}
}
setInterval(carousel,1000);
img {
width: 100px;
margin-left: 10px;
transition: .2s;
}
.active {
transform: scale(1.1);
}
<div class="container">
<img src="https://i.stack.imgur.com/cb20A.png" class="active"/>
<img src="https://i.stack.imgur.com/cb20A.png"/>
<img src="https://i.stack.imgur.com/cb20A.png"/>
<img src="https://i.stack.imgur.com/cb20A.png"/>
</div>
You can then replace the .active class by whatever you want.

Javascript slideshow not working - Appearance is incorrect

I'm having some issues with a slideshow banner for the top of my webpage. I tried following the W3 tutorial on it but not having much luck. So, below is my code:
HTML:
<div class="slide-content" style="max-width:1000px"> <img class="slidepic" src="testheadphoto.jpg" style="width:100%" border="0" /> <img class="slidepic" src="testphototwo.jpg" style="width:100%" border="0" />
<div class="slide-center slide-section slide-large slide-text-white slide-display-bottommiddle" style="width:100%">
<div class="slide-left slide-padding-left slide-hover-text-khaki" onclick="plusDivs(-1)">❮</div>
<div class="slide-right slide-padding-right slide-hover-text-khaki" onclick="plusDivs(-1)">❯</div>
<span class="slide-stamp demo slide-border slide-transparent slide-hover-white" _="_" span="span"> <span class="slide-stamp demo slide-border slide-transparent slide-hover-white" onclick="currentDiv(1)"></span> <span class="slide-stamp demo slide-border slide-transparent slide-hover-white" onclick="currentDiv(2)"></span> </span> </div>
CSS:
.slide {
display:none;
}
.slide-left, .slide-right, .slide-stamp {
cursor: pointer;
}
.slide-stamp {
height: 13px;
width: 13px;
padding: 0;
}
Javascript:
var slideIndex = 1;
showDivs(slideIndex);
function plusDivs(n) {
showDivs(slideIndex += n);
}
function currentDiv(n) {
showDivs(slideIndex = n);
}
function showDivs(n) {
var i;
var x = document.getElementsByClassName("slidepic");
var dots = document.getElementsByClassName("demo");
if (n > x.length) {slideIndex = 1}
if (n < 1) {slideIndex = x.length}
for (i = 0; i < x.length; i++) {
x[i].style.display = "none";
}
for (i = 0; i < dots.length; i++) {
dots[i].className = dots[i].className.replace(" slide-white", "");
}
x[slideIndex-1].style.display = "block";
dots[slideIndex-1].className += " slide-white";
}
At the moment, the two images are both appearing on the page at the same time and the arrows for right and left are below them. When you click an arrow, one of the pictures disappears and the slide effectively does work. It doesn't look how it's supposed to though. I've attached two images (One of how the slide should look and the other, how mine looks. As always, any help is much appreciated!
Thanks
Your images are line breaking because the container is not setup right.
In your CSS, try:
.slide-content {
white-space: nowrap;
}
I also recommend putting your controls into this same container and positioning them absolutely to their respective corners, with a z-index set to ensure they'll be on top of the images that are sliding in.

Categories