If an element has the class .stagger then I'm trying to execute an animation which shows the cards one by one.
In my current demo, the cards all fade in together. Even though I've specified a delay?
How can I the cards to appear one by one?
const boxes = gsap.utils.toArray('.stagger');
boxes.forEach((box, i) => {
const anim = gsap.fromTo(box, {
autoAlpha: 0,
y: 50
}, {
duration: 1,
autoAlpha: 1,
y: 0,
delay: 0.5,
});
ScrollTrigger.create({
trigger: box,
animation: anim,
toggleActions: 'play none none none',
once: true,
});
});
.section{
background: lightblue;
padding: 100px 0;
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/ScrollTrigger.min.js"></script>
<div class="section">
<div class="container">
<div class="row">
<div class="col-4">
<div class="card text-center stagger">
Card
</div>
</div>
<div class="col-4">
<div class="card text-center stagger">
Card
</div>
</div>
<div class="col-4">
<div class="card text-center stagger">
Card
</div>
</div>
</div>
</div>
</div>
In my current demo, the cards all fade in together. Even though I've specified a delay?
Each card has a 0.5s delay, so the'll move all together.
Use a delay based on the current card index:
delay: 0.5 + (0.5 * i)
So every card will be delayed with (0.5s + half a second for each index)
const boxes = gsap.utils.toArray('.stagger');
boxes.forEach((box, i) => {
const anim = gsap.fromTo(box, {
autoAlpha: 0,
y: 50
}, {
duration: 1,
autoAlpha: 1,
y: 0,
delay: 0.5 + (0.5 * i),
});
ScrollTrigger.create({
trigger: box,
animation: anim,
toggleActions: 'play none none none',
once: true,
});
});
.section{
background: lightblue;
padding: 100px 0;
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/ScrollTrigger.min.js"></script>
<div class="section">
<div class="container">
<div class="row">
<div class="col-4">
<div class="card text-center stagger">
Card
</div>
</div>
<div class="col-4">
<div class="card text-center stagger">
Card
</div>
</div>
<div class="col-4">
<div class="card text-center stagger">
Card
</div>
</div>
</div>
</div>
</div>
Related
I want the counter to start when I reach it during the scroll of the page. I'm using the same code than I found on Codepen, I checked if there is some problem in the html but I didn't find anything. I also looked for similar problems in the forum but I found nothing I could have been able to success with.
Basically the counter only works properly the first time one scrolls the page, if the page gets reloaded (tested in Firefox and Chrome) the counter doesn't start, it only starts with Firefox if after the reloading of the page, instead of restarting from the top of the page, the window is already showing the counter at the bottom of the page.
This is the code, where might I have gone wrong?
var a = 0;
$(window).scroll(function() {
var oTop = $('#counter').offset().top - window.innerHeight;
if (a == 0 && $(window).scrollTop() > oTop) {
$('.counter-value').each(function() {
var $this = $(this),
countTo = $this.attr('data-count');
$({
countNum: $this.text()
}).animate({
countNum: countTo
},
{
duration: 2000,
easing: 'swing',
step: function() {
$this.text(Math.floor(this.countNum));
},
complete: function() {
$this.text(this.countNum);
//alert('finished');
}
});
});
a = 1;
}
});
body {
background-color: black;
color: white;
height: 100vh;
box-sizing: border-box;
}
#counters {
font-family: 'Space Mono', monospace;
font-size: 4rem;
font-weight: 700;
}
#counter {
position:relative;
font-family: 'Space Mono', monospace;
font-size: 4rem;
font-weight: 700;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Through Fear</title>
<link href="https://fonts.googleapis.com/css?family=Work+Sans:400,600,700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Space+Mono:400,700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/style.css">
</head>
<body>
<div class="container">
<section class="children">
<div class="row break"> </div>
<div class="row"><div class="three columns">s</div>
<div class="six columns longtextsonblack">
<p>sss<br>
sss</p></div>
<div class="three columns">s</div>
</div>
</section>
<section class="children">
<div class="row break"> </div>
<div class="row"><div class="three columns">s</div>
<div class="six columns longtextsonblack">
<p>
sss</p></div>
<div class="three columns">s</div>
</div>
</section>
<section class="children"><div class="row break"> </div><div class="row"><div class="one column">s</div>
<div class="one column">s</div>
<div class="one column testiVerticali"><p>Fatturato</p></div>
<div class="six columns longtextsonblack translate">
<p>
sss</p>
<p>
sss</p></div>
<div class="three columns">s</div>
<div class="row">
<div class="one column centocinquanta">€</div>
<!--
<div id="counters" class="eight columns"><div class="counter"data-target="150000000000">0
</div>
</div>
-->
<div id="counter" class="eight columns counter-value" data-count="150000000">0
</div>
<div class="two columns">s</div>
</div>
</div>
</section>
<div class="row"><div class="three columns">s</div>
<div class="six columns longtextsonblack">
<p>
sss</p></div>
<div class="three columns">s</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="/js/index.js" type="module"></script>
<script src="http://code.jquery.com/jquery-2.1.3.min.js"></script>
<script src="/js/jquery.waypoints.min.js"></script>
</body>
</html>
The problem is occurring because you are scrolling on the container class, but detecting scrolls on the main document.
Assuming the container class is meant to take up the whole page, removing overflow: scroll; from its styling fixes the problem.
This causes the whole document to be scrolled instead of just the container class.
I have wrote the code below to get animated counters starting when visible on the window. It works well when the counters are all visible on the same row, but if only the first one is visible, this one will start the animation, but the others won't even if we scroll down. The first one is complete, but the others remain to zero.
/* SCROLL FUNCTIONS */
// Every time the window is scrolled...
$(window).scroll(function() {
// Check the location of each desired element
$('.counter').each(function(i) {
var bottom_of_object = $(this).offset().top + $(this).outerHeight();
var bottom_of_window = $(window).scrollTop() + $(window).height();
// If the object is completely visible in the window, fade it it
if (bottom_of_window > bottom_of_object) {
var $this = $(this);
$({
Counter: 0
}).animate({
Counter: $this.attr('data-to')
}, {
duration: 2000,
easing: 'swing',
step: function() {
$this.text(Math.ceil(this.Counter));
},
complete() {
$this.text(Math.ceil(this.Counter));
}
});
$(window).off("scroll");
}
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="row">
<div class="col">
<div class="row counters text-dark">
<div class="col-sm-6 col-lg-3 mb-4 mb-lg-0">
<div class="counter" data-to="30000">0</div>
<label>Happy Clients</label>
</div>
<div class="col-sm-6 col-lg-3 mb-4 mb-lg-0">
<div class="counter" data-to="15">0</div>
<label>Years in Business</label>
</div>
<div class="col-sm-6 col-lg-3 mb-4 mb-sm-0">
<div class="counter" data-to="352">0</div>
<label>Cups of Coffee</label>
</div>
<div class="col-sm-6 col-lg-3">
<div class="counter" data-to="178">0</div>
<label>High Score</label>
</div>
</div>
</div>
</div>
The problem is this line of code:
$(window).off("scroll");
Your off call unbinds all events, not just one. That means all scroll event bindings are lost after the first number animation executes.
To solve this, you need to bind and unbind each number's animation separately. A simple way to do this would be to have a different function for each number animation and bind/unbind them separately. A generic example:
var myScroll1 = function () {
$(window).off("scroll", myScroll1)
}
$(window).on("scroll", myScroll1)
Notice we are turning on and off just this specific function reference. You can have 4 of them and switch them on and off separately.
EDIT: Here's your script modified to work as explained:
var anim1 = function () { animateAndKill(1, $("#n1"), 3000, anim1); }
var anim2 = function () { animateAndKill(2, $("#n2"), 15, anim2); }
var anim3 = function () { animateAndKill(3, $("#n3"), 352, anim3); }
var anim4 = function () { animateAndKill(4, $("#n4"), 178, anim4); }
// Every time the window is scrolled...
function animateAndKill(id, $number, max, myFunction) {
var bottom_of_object = $number.offset().top + $number.outerHeight();
var bottom_of_window = $(window).scrollTop() + window.innerHeight;
// If the object is completely visible in the window, fade it it
if (bottom_of_window > bottom_of_object) {
$({ Counter: 0 }).animate({ Counter: max }, {
duration: 2000,
easing: 'swing',
step: function () {
var n = Math.ceil(this.Counter);
$number.html(n);
}
});
$(window).off("scroll", myFunction);
}
}
$(window).on("scroll", anim1);
$(window).on("scroll", anim2);
$(window).on("scroll", anim3);
$(window).on("scroll", anim4);
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="code.js"></script>
</head>
<body>
<div style="height: 1000px; background: #33FF44"></div>
<div class="row" style="z-index: 100; font-size: 100px;">
<div class="col">
<div class="row counters text-dark">
<div class="col-sm-6 col-lg-3 mb-4 mb-lg-0">
<div id="n1" class="counter" data-to="30000">0</div>
<label>Happy Clients</label>
</div>
<div class="col-sm-6 col-lg-3 mb-4 mb-lg-0">
<div id="n2" class="counter" data-to="15">0</div>
<label>Years in Business</label>
</div>
<div class="col-sm-6 col-lg-3 mb-4 mb-sm-0">
<div id="n3" class="counter" data-to="352">0</div>
<label>Cups of Coffee</label>
</div>
<div class="col-sm-6 col-lg-3">
<div id="n4" class="counter" data-to="178">0</div>
<label>High Score</label>
</div>
</div>
</div>
</div>
<div style="height: 3000px; background: #33FF44"></div>
</body>
</html>
https://jsfiddle.net/tyddlywink/pdvh4b3n/
Get rid of the $(window).off("scroll");bit. And keep track of who's already been counted or not.
<div class="row">
<div class="col">
<div class="row counters text-dark">
<div class="col-sm-6 col-lg-3 mb-4 mb-lg-0">
<div class="counter" data-to="30000" data-counted='false'>0</div>
<label>Happy Clients</label>
</div>
<div style="height: 750px">
</div>
<div class="col-sm-6 col-lg-3 mb-4 mb-lg-0">
<div class="counter" data-to="15" data-counted='false'>0</div>
<label>Years in Business</label>
</div>
<div style="height: 750px">
</div>
<div class="col-sm-6 col-lg-3 mb-4 mb-sm-0">
<div class="counter" data-to="352" data-counted='false'>0</div>
<label>Cups of Coffee</label>
</div>
<div style="height: 750px">
</div>
<div class="col-sm-6 col-lg-3">
<div class="counter" data-to="178" data-counted='false'>0</div>
<label>High Score</label>
</div>
</div>
</div>
</div>
Javascript:
// Every time the window is scrolled...
$(window).scroll(function() {
// Check the location of each desired element
$('.counter').each(function(i) {
var bottom_of_object = $(this).offset().top + $(this).outerHeight();
var bottom_of_window = $(window).scrollTop() + $(window).height();
var counted = $(this).data("counted");
// If the object is completely visible in the window, fade it it
if (!counted && bottom_of_window > bottom_of_object) {
$(this).data("counted", true);
var $this = $(this);
$({
Counter: 0
}).animate({
Counter: $this.attr('data-to')
}, {
duration: 2000,
easing: 'swing',
step: function() {
$this.text(Math.ceil(this.Counter));
},
complete() {
$this.text(Math.ceil(this.Counter));
}
});
}
});
});
/*
SCROLL FUNCTIONS
********************************/
// Every time the window is scrolled...
$(window).scroll(function () {
// Check the location of each desired element
$('.count').each(function (i) {
var bottom_of_object = $(this).offset().top + $(this).outerHeight();
var bottom_of_window = $(window).scrollTop() + $(window).height();
// If the object is completely visible in the window, fade it it
if (bottom_of_window > bottom_of_object) {
var $this = $(this);
$({
Counter: 0
}).animate({
Counter: $this.attr('data-to')
}, {
duration: 2000,
easing: 'swing',
step: function () {
$this.text(Math.ceil(this.Counter));
},
complete(){
$this.text(Math.ceil(this.Counter));
}
});
$(this).removeClass('count').addClass('counted');
}
});
});
<div class="row">
<div class="col">
<div class="row counters text-dark">
<div class="col-sm-6 col-lg-3 mb-4 mb-lg-0">
<div class="count" data-to="30000">0</div>
<label>Happy Clients</label>
</div>
<div class="col-sm-6 col-lg-3 mb-4 mb-lg-0">
<div class="count" data-to="15">0</div>
<label>Years in Business</label>
</div>
<div class="col-sm-6 col-lg-3 mb-4 mb-sm-0">
<div class="count" data-to="352">0</div>
<label>Cups of Coffee</label>
</div>
<div class="col-sm-6 col-lg-3">
<div class="count" data-to="178">0</div>
<label>High Score</label>
</div>
</div>
</div>
</div>
Listening to scroll event is not performance friendly, you should really consider using Intersection Observer for stuff like this.
First you have to create a new observer:
var options = {
rootMargin: '0px',
threshold: 1.0
}
var observer = new IntersectionObserver(callback, options);
Here we define that once your target Element is 100% visible in the viewport (threshold of 1) your callback Function is getting executed. Here you can define another percentage, 0.5 would mean that the function would be executed once your element is 50% visible.
Then you have to define which elements to watch, in your case this would be the counter elements:
var target = document.querySelector('.counter');
observer.observe(target);
Last you need to specify what should happen once the element is visible in your viewport by defining the callback function:
var callback = function(entries, observer) {
entries.forEach(entry => {
// Each entry describes an intersection change for one observed
// here you animate the counter
});
};
In your specific case you probably won't run into performance problems but if you have more and more elements you will start to notice something. So it's better to know of this and to "do it right" if you come across this problem again.
If you need to support older browsers, use the official polyfill from w3c.
You can also remove the observer from any element if you don't need element where
I have created a counter section on my site which animates on page load, however I am trying to trigger the animation when the user gets to that section.
Currently I have this, however the animation only triggers when the div is beyond the nav, ie: the top of the screen including the nav. How would I change this so that the animation triggers as the div becomes visible?
I'm also having an issue that it shows to start as the full number without the commas, and then goes to 0 and animates, how can I make it show a 0 to begin with?
I'm pretty new to JS so would appreciate any explanation into this.
Heres what I have:
const convert = str => {
// Find the number
let regx = /(\d{1,3})(\d{3}(?:,|$))/;
// Set a variable
let currStr;
// Start loop
do {
// Replace current string, split it
currStr = (currStr || str.split(`.`)[0])
.replace(regx, `$1,$2`)
} while (currStr.match(regx)); // Loop
// Return our result from function
return (str.split(`.`)[1]) ?
currStr.concat(`.`, str.split(`.`)[1]) :
currStr;
};
$(window).scroll(startCounter);
function startCounter() {
if ($(window).scrollTop() > $('#counter').offset().top) {
$(window).off("scroll", startCounter);
$('.count').each(function() {
$(this).prop('Counter', 0).animate({
Counter: $(this).text()
}, {
duration: 2000,
easing: 'swing',
step: function(now) {
$(this).text(Math.ceil(now));
$(this).text(convert($(this).text()))
}
});
});
}
}
.section-counter {
margin-top: 150vh;
margin-bottom: 150vh;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section class="section-counter" id="counter">
<div class="row">
<h2>Headline Data Figures</h2>
</div>
<div class="row">
<div class="col span-1-of-2">
<div class="row">
<div id="shiva"><span class="count">1688019</span>
<h3>Contributions</h3>
</div>
</div>
<div id="shiva"><span class="count">82150</span>
<h3>Items of Business</h3>
</div>
</div>
<div class="col span-1-of-2">
<div class="row">
<div id="shiva"><span class="count">10505</span>
<h3>Meetings</h3>
</div>
</div>
<div id="shiva"><span class="count">168260</span>
<h3>Written/Oral Questions</h3>
</div>
</div>
</div>
<div class="row arrow-dark arrow__7 animated pulse infinite">
<i class="ion-md-arrow-dropdown"></i>
</div>
</section>
If I understand correctly, the problem is that you're comparing the offset of the div to the top of the screen, when actually you'd want to find out where the bottom of the screen is, and compare that to the position of the div.
Regarding starting from 0, you can have the elements use a data attribute to determine the max instead of the text, that way you can have the elements read 0 until they start counting:
const convert = str => {
// Find the number
let regx = /(\d{1,3})(\d{3}(?:,|$))/;
// Set a variable
let currStr;
// Start loop
do {
// Replace current string, split it
currStr = (currStr || str.split(`.`)[0])
.replace(regx, `$1,$2`)
} while (currStr.match(regx)); // Loop
// Return our result from function
return (str.split(`.`)[1]) ?
currStr.concat(`.`, str.split(`.`)[1]) :
currStr;
};
$(window).scroll(startCounter);
function startCounter() {
var bottomOfScreen = $(window).scrollTop() + $(window).innerHeight();
if (bottomOfScreen > $('#counter').offset().top) {
$(window).off("scroll", startCounter);
$('.count').each(function() {
$(this).prop('Counter', 0).animate({
Counter: $(this).attr('data-max')
}, {
duration: 2000,
easing: 'swing',
step: function(now) {
$(this).text(Math.ceil(now));
$(this).text(convert($(this).text()))
}
});
});
}
}
.section-counter {
margin-top: 150vh;
margin-bottom: 150vh;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section class="section-counter" id="counter">
<div class="row">
<h2>Headline Data Figures</h2>
</div>
<div class="row">
<div class="col span-1-of-2">
<div class="row">
<div id="shiva"><span class="count" data-max="1688019">0</span>
<h3>Contributions</h3>
</div>
</div>
<div id="shiva"><span class="count" data-max="812150">0</span>
<h3>Items of Business</h3>
</div>
</div>
<div class="col span-1-of-2">
<div class="row">
<div id="shiva"><span class="count" data-max="10505">0</span>
<h3>Meetings</h3>
</div>
</div>
<div id="shiva"><span class="count" data-max="168260">0</span>
<h3>Written/Oral Questions</h3>
</div>
</div>
</div>
<div class="row arrow-dark arrow__7 animated pulse infinite">
<i class="ion-md-arrow-dropdown"></i>
</div>
</section>
I will give you the logic to kickstart an animation once the user gets to an element.
The relevant event trigger is the ONMOUSEOVER, so to incorporate it...
<div id="abc" onmouseover="AnimateIt();"></div>
Needless to mention that our element must have its animation PAUSED in its styling to begin with, like this...
animation-play-state:paused;
So the JavaScript logic to animate...
function AnimateIt(){ abc.style.animationPlayState='running'; }
That's it.
I'm building a system that would allow a user to drag and drop elements from a dragzone to a dropzone. Once dropped, the elements are cloned "back" to their origin. Also, the user can sort the elements dropped as he wants.
I had a first issue where I couldn't clone the block I dragged, but I could sort it when it was dropped. Now that I fixed the clone problem, if I try to sort the elements, the elements moving come from the dragzone and I can't understand why.
Here is the HTML:
<div class="container-fluid">
<div class="row">
<div class="card col-3">
<div class="card-body">
<div class="card draggable-element" data-target="textarea">
<div class="card-body">
This is some text within a card body. 1
</div>
</div>
<div class="card draggable-element" data-target="textfield">
<div class="card-body">
This is some text within a card body. 2
</div>
</div>
<div class="card draggable-element" data-target="fileinput">
<div class="card-body">
This is some text within a card body. 3
</div>
</div>
</div>
</div>
<div class="card col-6 offset-1">
<div class="card-body dropzone">
</div>
</div>
</div>
</div><!-- /.container -->
And here is the JS:
$(document).ready(function() {
$('.draggable-element').draggable({
revert: 'invalid',
appendTo: '.dropzone',
helper: 'clone'
});
$('.dropzone').droppable({
drop: function (event, ui) {
// With the following code, the elements won't get cloned
var item = $(ui.draggable);
if(!item.hasClass('copy')) {
item.clone().addClass('copy');
item.draggable({
revert: 'invalid',
stack: ".draggable",
helper: 'clone'
})
}
else {
$(this).append($(ui.helper).clone());
}
$(this).append(item);
}
})
.sortable();
});
/*
drop: function (event, ui) {
// With the following code, the elements getting sorted are
// the div.draggable-element from the div.card.col-3
$(ui.draggable).clone(true).detach().css({
position: 'relative',
top: 'auto',
left: 'auto'
}).appendTo(this);
}
*/
I haven't used jquery-ui for a while so I can't find what may be obvious, I tried to mix the code together but it didn't end up well at all.
Thank you in advance
OK, this is likely NOT a full answer ( but the markup had an odd "card-body" holder of cards so I renamed that to test. Does Not "clone" as was represented in the question...so it sorts in my example but not sure this totally reproduces/resolves here. I updated the "clone" part but not sure it is what you desire.
$(document).ready(function() {
$('.draggable-element').draggable({
revert: 'invalid',
appendTo: '.dropzone',
helper: 'clone'
});
$('.dropzone').droppable({
drop: function(event, ui) {
// With the following code, the elements won't get cloned
var item = $(ui.draggable);
// hide to not obscure console.log(item.length);
if (!item.hasClass('copy')) {
var newy = item.clone(false);
newy.addClass('copy');
//console.log(newy);
newy.draggable({
revert: 'invalid',
stack: ".draggable",
helper: 'clone'
});
} else {
$(this).append(item);
}
$('.dropzone').append(newy);
// $(this).append(item);
}
})
.sortable();
});
.cards,
.dropzone {
border: solid red 1px;
height: 5em;
}
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<div class="container-fluid">
<div class="row">
<div class="card col-3">
<div class="cards">
<div class="card draggable-element" data-target="textarea">
<div class="card-body">
This is some text within a card body. 1
</div>
</div>
<div class="card draggable-element" data-target="textfield">
<div class="card-body">
This is some text within a card body. 2
</div>
</div>
<div class="card draggable-element" data-target="fileinput">
<div class="card-body">
This is some text within a card body. 3
</div>
</div>
</div>
</div>
<div class="card col-6 offset-1">
<div class="card-body dropzone">
</div>
</div>
</div>
</div>
I'm using bootstrap carousel for the slider , on each slide there is some text over it.
I would like that text over the slides to aappear letter by letter.
I have almost solved it..
But there are 2 issues
On first slide the text doesn't come up at all
If the user goes to some other tab on the browser means if the current window is not in focus ,then everything gets messed up.
Here is my fiddle
HTML
<main>
<div class="container">
<div class="block-wrap">
<div id="js-carousel" class="carousel slide" data-ride="carousel">
<!-- Wrapper for slides -->
<div class="carousel-inner" role="listbox">
<div class="item active">
<img src="http://cdn.theatlantic.com/assets/media/img/photo/2015/11/images-from-the-2016-sony-world-pho/s01_130921474920553591/main_900.jpg?1448476701" alt="Chania">
<div class="caption">
<div class="mystring hide">companies with Inbound Marketing</div>
<h4>We help <div class="demo-txt"></div> </h4>
</div>
</div>
<div class="item">
<img src="http://cdn.theatlantic.com/assets/media/img/photo/2015/11/images-from-the-2016-sony-world-pho/s01_130921474920553591/main_900.jpg?1448476701" alt="Chania">
<div class="caption">
<div class="mystring hide">companies with Inbound Marketing</div>
<h4>We help <div class="demo-txt "></div> </h4>
</div>
</div>
<div class="item">
<img src="http://cdn.theatlantic.com/assets/media/img/photo/2015/11/images-from-the-2016-sony-world-pho/s01_130921474920553591/main_900.jpg?1448476701" alt="Flower">
<div class="caption">
<div class="mystring hide">2companies with Inbound Marketing</div>
<h4>We help <div class="demo-txt"></div> </h4>
</div>
</div>
<div class="item">
<img src="http://cdn.theatlantic.com/assets/media/img/photo/2015/11/images-from-the-2016-sony-world-pho/s01_130921474920553591/main_900.jpg?1448476701" alt="Flower">
<div class="caption">
<div class="mystring hide">3companies with Inbound Marketing</div>
<h4>We help <div class="demo-txt"></div> </h4>
</div>
</div>
<div class="overlay-effect"></div>
</div>
</div>
</div>
</div>
</main>
JS:
$(document).ready(function() {
$('#js-carousel').carousel({
interval: 5000
});
$('#js-carousel').on('slid.bs.carousel', function () {
var showText = function (target, message, index, interval) {
if (index < message.length) {
$(target).append(message[index++]);
setTimeout(function () { showText(target, message, index, interval); }, interval);
}
}
var str = $(this).find(".active .mystring").html();
$('.active .demo-txt').html("");
showText(".active .demo-txt", str, 0, 100);
});
});
Instead of settimeout use setInterval function. Also use clearInterval to clear schedular when a new slide begin. (I think this is your second problem.)
Here is your target js file:
$(document).ready(function() {
$('#js-carousel').carousel({
interval: 5000
});
var handler;
var interval = 5000;
var index = 0;
function showText(target, message, index, interval) {
if (index < message.length) {
$(target).append(message[index]);
}
}
function iteration() {
if(handler){
clearInterval(handler);
}
index = 0;
var str = $(this).find(".active .mystring").html();
$('.active .demo-txt').html("");
showText(".active .demo-txt", str, index++, 100);
handler = setInterval(function(){
showText(".active .demo-txt", str, index++, 100);
}, 100);
}
//on each carousel slide change:
$('#js-carousel').on('slid.bs.carousel', iteration);
//start immediately for your first problem:
iteration.bind($('#js-carousel'))();
});
It's because your function is inside the slide event. At starting, the carousel doesn't slide...
Fiddle: https://jsfiddle.net/Lbasa2jh/5/
JS:
$(document).ready(function() {
var showText = function (target, message, index, interval) {
if (index < message.length) {
$(target).append(message[index++]);
setTimeout(function () { showText(target, message, index, interval); }, interval);
}
};
$('#js-carousel').carousel({
interval: 5000
});
var str0 = $(this).find(".active .mystring").html();
$('.active .demo-txt').html("");
showText(".active .demo-txt", str0, 0, 100);
$('#js-carousel').on('slid.bs.carousel', function () {
var str = $(this).find(".active .mystring").html();
$('.active .demo-txt').html("");
showText(".active .demo-txt", str, 0, 100);
});
});
The timer can be tricky when the tab goes inactive. I moved code around and cleared the timeout to make sure the timeout is clean when starting a new slide.
I noticed an issue (not always) when switching back from a different tab is that the carousel actually moves to next slide quicker than 5 second causing the text to be incomplete.
https://jsfiddle.net/vLwm58Ln/
$(document).ready(function() {
$('#js-carousel').carousel({
interval: 5000
});
var showText = function(target, message, index, interval) {
if (index < message.length) {
$(target).append(message[index++]);
timer = setTimeout(function() {
showText(target, message, index, interval);
}, interval);
}
}, timer;
//First time, this triggers right away instead of waiting for the slide to move
showText(".active .demo-txt", $('#js-carousel').find(".active .mystring").html(), 0, 100);
$('#js-carousel').on('slid.bs.carousel', function() {
//clear any messed up timeout from prev slide
clearTimeout(timer);
//clear message that may be incomplete from the previous text animation
$('.prevActive').removeClass('prevActive').html('');
var str = $(this).find(".active .mystring").html();
$('.active .demo-txt').addClass('prevActive').html("");
showText(".active .demo-txt", str, 0, 100);
});
});
.carousel-inner {
position: relative;
}
.carousel-inner .overlay-effect {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.6);
}
.carousel-inner .caption {
color: #ffffff;
font-weight: bold;
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
height: 100px;
z-index: 9999;
left: 5%;
}
.carousel-inner .caption h1,
.carousel-inner .caption h2 {
font-weight: bold;
line-height: 1.6;
}
.carousel-inner .caption h1 {
font-size: 64px;
}
.carousel-inner .caption h2 {
font-size: 44px;
}
.carousel-inner .demo-txt {
border-bottom: 4px solid #ec8422;
padding-bottom: 5px;
}
<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.2.js"></script>
<script type="text/javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<main>
<div class="container">
<div class="block-wrap">
<div id="js-carousel" class="carousel slide" data-ride="carousel">
<!-- Wrapper for slides -->
<div class="carousel-inner" role="listbox">
<div class="item active">
<img src="http://cdn.theatlantic.com/assets/media/img/photo/2015/11/images-from-the-2016-sony-world-pho/s01_130921474920553591/main_900.jpg?1448476701" alt="Chania">
<div class="caption">
<div class="mystring hide">companies with Inbound Marketing</div>
<h4>We help <div class="demo-txt"></div> </h4>
</div>
</div>
<div class="item">
<img src="http://cdn.theatlantic.com/assets/media/img/photo/2015/11/images-from-the-2016-sony-world-pho/s01_130921474920553591/main_900.jpg?1448476701" alt="Chania">
<div class="caption">
<div class="mystring hide">companies with Inbound Marketing</div>
<h4>We help <div class="demo-txt "></div> </h4>
</div>
</div>
<div class="item">
<img src="http://cdn.theatlantic.com/assets/media/img/photo/2015/11/images-from-the-2016-sony-world-pho/s01_130921474920553591/main_900.jpg?1448476701" alt="Flower">
<div class="caption">
<div class="mystring hide">2companies with Inbound Marketing</div>
<h4>We help <div class="demo-txt"></div> </h4>
</div>
</div>
<div class="item">
<img src="http://cdn.theatlantic.com/assets/media/img/photo/2015/11/images-from-the-2016-sony-world-pho/s01_130921474920553591/main_900.jpg?1448476701" alt="Flower">
<div class="caption">
<div class="mystring hide">3companies with Inbound Marketing</div>
<h4>We help <div class="demo-txt"></div> </h4>
</div>
</div>
<div class="overlay-effect"></div>
</div>
</div>
</div>
</div>
</main>