Reputation: 352
I'm modifying A Better Simple Slideshow to have pausing capability. I've managed to separate pausing and unpausing out of autoCycle() into their own methods (pauseCycle and unpauseCycle), so I can use them for the pause control as well as autoCycle(). I've created the addPause() method to generate the pause control. The addPause() method successfully pauses the slideshow, but does something funky with the setInterval() or the interval var being passed to it when unpausing the slideshow. It appears to result in an additional setInterval() running simultaneously. Here's the code:
var aFSlides = function(el, options) {
var $slideshows = document.querySelectorAll(el), // a collection of all of the slideshows
$slideshow = {},
Slideshow = {
init: function(el, options) {
this.counter = 0; // to keep track of current slide
this.interval = 0; // to control autoCycle
this.paused = 0; // to keep track of whether paused or not
this.el = el; // current slideshow container
this.$items = el.querySelectorAll('figure'); // a collection of all of the slides, caching for performance
this.numItems = this.$items.length; // total number of slides
options = options || {}; // if options object not passed in, then set to empty object
// options.auto = options.auto || false; // if options.auto object is not passed in, then set to false
this.opts = {
auto: (typeof options.auto === "undefined") ? false : options.auto,
speed: (typeof options.speed === "undefined") ? 6000 : options.speed,
pause: (typeof options.pause === "undefined") ? false : options.pause,
pauseOnHover: (typeof options.pauseOnHover === "undefined") ? true : options.pauseOnHover,
};
this.$items[0].classList.add('current'); // add .current class to first figure
if (this.opts.auto) {
this.autoCycle(this.el, this.opts.speed, this.opts.pauseOnHover);
}
if (this.opts.pause) {
this.addPause(this.el);
}
},
showCurrent: function(i) {
// increment or decrement this.counter depending on whether i === 1 or i === -1
if (i > 0) {
this.counter = (this.counter + 1 === this.numItems) ? 0 : this.counter + 1;
} else {
this.counter = (this.counter - 1 < 0) ? this.numItems - 1 : this.counter - 1;
}
// remove .show from whichever element currently has it
// http://stackoverflow.com/a/16053538/2006057
[].forEach.call(this.$items, function(el) {
el.classList.remove('current');
});
// add .show to the one item that's supposed to have it
this.$items[this.counter].classList.add('current');
},
pauseCycle: function(el, speed) {
var that = this;
interval = clearInterval(interval);
el.classList.add('paused');
},
unpauseCycle: function(el, speed) {
var that = this;
interval = window.setInterval(function() {
that.showCurrent(1); // increment & show
}, speed);
el.classList.remove('paused');
},
addPause: function(el, speed) {
var spanPause = document.createElement("span"),
docFrag2 = document.createDocumentFragment(),
that = this,
thatSpeed = speed;
spanPause.classList.add('pause');
spanPause.innerHTML = 'Pause';
docFrag2.appendChild(spanPause);
el.appendChild(docFrag2);
togglePause = function(el, speed) {
if (that.paused == 1) {
var speed = that.opts.speed;
that.unpauseCycle(el, speed);
that.paused = 0;
return that.paused;
} else if (that.paused == 0) {
var speed = that.opts.speed;
interval = clearInterval(interval);
that.pauseCycle(el, speed);
that.paused = 1;
return that.paused;
}
}
el.querySelector('.pause').addEventListener('click', function() {
togglePause(el, speed);
}, false);
},
autoCycle: function(el, speed, pauseOnHover) {
var that = this;
if (that.paused == 0) {
that.unpauseCycle(el, speed);
}
if (pauseOnHover) {
el.addEventListener('mouseover', function() {
if (that.paused == 0) {
that.pauseCycle(el, speed);
}
}, false);
el.addEventListener('mouseout', function() {
if (that.paused == 0) {
that.unpauseCycle(el, speed);
}
}, false);
} // end pauseonhover
} // end autoCycle
}; // end Slideshow object .....
// make instances of Slideshow as needed
[].forEach.call($slideshows, function(el) {
$slideshow = Object.create(Slideshow);
$slideshow.init(el, options);
});
};
/* Init for this example snippet */
var aFS56c641d29d032 = {
auto: true,
speed: 2000,
pause: true,
};
aFSlides('.aFS56c641d29d032', aFS56c641d29d032);
body {
font: 400 10px/1.3 Menlo, Courier, sans-serif;
}
figure {
display: none;
}
figure.current {
display: block;
}
figure pre {
font: 700 24px/1.3 Menlo, Courier, sans-serif;
white-space: pre;
}
span {
background: #f66;
color: #fff;
font: 700 16px/1.3 Menlo, Courier, sans-serif;
padding: 10px 20px;
display: inline-block;
}
<div id="smile-gallery" class="aFS56c641d29d032 ">
<div class="slides">
<figure class="slide" id="bna-1">
<div class="slide-content">
<pre>
________________________
| |
| |
| 0 0 |
| |
| \________/ |
| |
|________________________|
</pre>
</div>
</figure>
<figure class="slide" id="bna-2">
<div class="slide-content">
<pre>
________________________
| |
| |
| o O |
| |
| |
| ______/ |
|________________________|
</pre>
</div>
</figure>
<figure class="slide" id="bna-3">
<div class="slide-content">
<pre>
________________________
| |
| |
| ^ ^ |
| |
| |
| (EEEEE) |
|________________________|
</pre>
</div>
</figure>
<figure class="slide" id="bna-4">
<div class="slide-content">
<pre>
________________________
| |
| |
| | | |
| ____________ |
| \ / |
| \________/ |
|________________________|
</pre>
</div>
</figure>
</div>
<!-- /.slides -->
</div>
<!-- /#smile-gallery -->
<p>
Taken from <a href="https://github.com/leemark/better-simple-slideshow" target="_blank">A Better Simple Slideshow</a>.
</p>
As you run this script, if you play with the Pause control, some weird stuff happens. First it pauses and disables the pauseOnHover condition, like it should. Then you click it again, it unpauses and starts advancing two slides at a time. The pauseOnHover works again, but only pauses one of the slide advancements on hover, so the slideshow is still advancing one slide at a time. Click it Pause again and it stops advancing by two times, but it continues advancing at one slide at a time (when it should now be paused). Click is again and it begins advancing three frames at time (two if you're hovering over it again) and so on. Something just keeps getting added on every time and I can't figure out what it is. Please help.
Thanks!
UPDATE 22 Feb 2016
Here's a JSFiddle I've been working on for this project. Getting both pause AND pauseOnHover to work at the same time has been a nightmare.
Upvotes: 0
Views: 710
Reputation: 352
I got it! Simple answer:
Stop multiplying it!
Solution #1: "Let it Run"
Don't stop setInterval()
. Instead, give it some variables to check. If it returns false
for any of them, don't execute the slide transition (or whatever it's supposed to execute in your case). setInterval()
will still be running, checking for those variables every time it fires. But every time it fires the variables will return false and it will never execute the code inside. When a click occurs or something changes the variable(s), then when it fires again, it will execute the code inside (the slide transition, etc.). setInterval()
never stops, so you never have to start it again and you never have to worry about how you're going to selecting the same instance of setInterval()
. See example below:
var aFSlides = function (el, options) {
var $slideshows = document.querySelectorAll(el), // a collection of all of the slideshow
$slideshow = {},
Slideshow = {
init: function (el, options) {
this.counter = 0; // to keep track of the current slide
this.interval = 0; // to keep track of whether slideshow is running or not
this.stopCycle = 0; // to keep track of whether the slideshow has been stopped or not
this.hovered = 0; // to keep track of whether the mouse is hovering over the slideshow or not
this.paused = 0; // to keep track of whether the pause control is set to "pause" or not
this.el = el; // current slideshow container
this.$items = el.querySelectorAll('figure'); // a collection of all of the slides, caching for performance
this.numItems = this.$items.length; // total number of slides
options = options || {}; // if options object not passed in, then set to empty object
this.opts = {
auto: (typeof options.auto === "undefined") ? false : options.auto,
speed: (typeof options.speed === "undefined") ? 6000 : options.speed,
pauseOnHover: (typeof options.pauseOnHover === "undefined") ? true : options.pauseOnHover,
pause: (typeof options.pause === "undefined") ? false : options.pause,
};
this.$items[0].classList.add('current'); // add show class to first figure
if (this.opts.prevNext) {
this.addPrevNext(this.el);
}
if (this.opts.pause) {
this.addPause(this.el);
}
this.addEventListeners(this.el,this.opts.speed);
if (this.opts.auto) {
this.unpauseCycle(this.el, this.opts.speed, this.opts.pauseOnHover);
}
if (this.opts.fullScreen) {
this.addFullScreen(this.el);
}
if (this.opts.swipe) {
this.addSwipe(this.el);
}
},
showCurrent: function (i) {
// increment or decrement this.counter depending on whether i === 1 or i === -1
if (i > 0) {
this.counter = (this.counter + 1 === this.numItems) ? 0 : this.counter + 1;
} else {
this.counter = (this.counter - 1 < 0) ? this.numItems - 1 : this.counter - 1;
}
// remove .show from whichever element currently has it
// http://stackoverflow.com/a/16053538/2006057
[].forEach.call(this.$items, function (el) {
el.classList.remove('current');
});
// add .show to the one item that's supposed to have it
this.$items[this.counter].classList.add('current');
},
addPause: function (el) {
// build and inject prev/next controls
// first create all the new elements
var spanPause = document.createElement("span"),
docFragPs = document.createDocumentFragment();
// add classes
spanPause.classList.add('pause');
// add contents
spanPause.innerHTML = 'Pause';
// append elements to fragment, then append fragment to DOM
docFragPs.appendChild(spanPause);
el.appendChild(docFragPs);
},
unpauseCycle: function (el,speed) {
var that = this;
this.interval = setInterval(function () {
if ((that.stopCycle != 1) && (that.hovered != 1)) {
that.showCurrent(1); // increment & show
}
}, speed);
},
addEventListeners: function (el,speed,pauseOnHover) {
var that = this;
if (that.opts.pauseOnHover) {
el.addEventListener('mouseover', function () {
that.hovered = 1;
return that.hovered;
}, false);
el.addEventListener('mouseout', function () {
that.hovered = 0;
return that.hovered;
}, false);
};
el.querySelector('.pause').addEventListener('click', function () {
if ((that.paused === 0) && (that.stopCycle != 1)) {
that.stopCycle = 1;
that.paused = 1;
el.classList.add('paused');
return that.stopCycle,that.paused;
}
else if ((that.paused === 1) || (that.stopCycle === 1)) {
that.stopCycle = 0;
that.paused = 0;
el.classList.remove('paused');
el.classList.remove('stopped');
that.interval;
return that.stopCycle,that.paused;
}
}, false);
},
}; // end Slideshow object .....
// make instances of Slideshow as needed
[].forEach.call($slideshows, function (el) {
$slideshow = Object.create(Slideshow);
$slideshow.init(el, options);
});
};
var aFS56c641d29d032 = {
auto: true,
speed: 2000,
prevNext: true,
pause: true,
swipe: true,
};
aFSlides('.aFS56c641d29d032', aFS56c641d29d032);
body {
font: 400 14px/1.3 Menlo,Courier,sans-serif;
}
figure {
display: none;
}
figure.current {
display: block;
}
figure p {
font: 700 24px/1.3 Menlo,Courier,sans-serif;
white-space: pre;
}
span {
font-weight: bold;
padding: 10px;
display: inline-block;
}
<body>
<div id="smile-gallery" class="aFS56c641d29d032 ">
<div class="slides">
<figure class="slide" id="bna-1">
<div class="slide-content">
<p>
________________________
| |
| |
| 0 0 |
| |
| \________/ |
| |
|________________________|
</p></div>
</figure>
<figure class="slide" id="bna-2">
<div class="slide-content">
<p>
________________________
| |
| |
| o O |
| |
| |
| ______/ |
|________________________|
</p></div>
</figure>
<figure class="slide" id="bna-3">
<div class="slide-content">
<p>
________________________
| |
| |
| ^ ^ |
| |
| |
| (EEEEE) |
|________________________|
</p></div>
</figure>
<figure class="slide" id="bna-4">
<div class="slide-content">
<p>
________________________
| |
| |
| | | |
| ____________ |
| \ / |
| \________/ |
|________________________|
</p></div>
</figure>
</div><!-- /.slides -->
</div><!-- /#smile-gallery -->
<p>
Taken from <a href="https://github.com/leemark/better-simple-slideshow">A Better Simple Slideshow</a>.
</p>
</body>
Solution #2: "Don't Loose Track of it"
The idea here is to create variables for everything first, then use them, and always in the right scope (of course). So here's how to use them:
After you define variables (in the outer most scope of the object, for easier coding), assign your setInterval()
to one of the variables (ex. myIntervalVar = setInterval( ... );
), then always reference it with that variable. So to clear it, clearInterval(myIntervalVar)
. Make sure you're in the right scope, so you may need to use:
clearInterval(this.myIntervalVar)
or:
var equalThis = this;
someFunction(){
clearInterval(equalThis.myIntervalVar);
}
Then any time you want to start/stop your setInterval()
, check first if it's already running with a variable:
if (this.myRunningVar === 0) {
this.myIntervalVar;
}
This of course means when you start setInterval()
you need to set your variable to keep track of it too:
myIntervalVar = setInterval( function () {
...
this.myRunningVar = 1;
return myRunningVar;
}, false);
and:
if (this.myRunningVar === 0) {
this.myIntervalVar;
this.myRunningVar = 1;
return myRunningVar;
}
and also whenever you stop it:
clearInterval(this.myIntervalVar)
this.myRunningVar = 0;
return myRunningVar;
This second solution could probably be elaborated on and should be more thoroughly tested. I went with the first solution because it was much easier and seemed cleaner to me.
Basically, the multiplying or stacking of setInterval()
s firing simultaneously comes from running the function (or functions) that start it more than once without successfully stopping it. And even if you successfully stop it with clearInterval()
it's still there (albeit nolonger firing). So if you have it assigned to a variable, all you need to do is call that variable and it will start again, picking up where it left off. Otherwise, if you newly try to define that variable again as a setInterval()
function, say with a function you keep calling, instead of resetting the variable with a new setInterval()
It just adds another setInterval()
on to it:
startCycle(){
myIntervalVar = setInterval( function () {
// whatever you want to execute each time it fires
}, false);
}
Calling startCycle()
more than once will result in more than one setInterval()
s running simultaneously, because each time you do, it adds setInterval()
to myIntervalVar
. And each time you set it or call the function that sets it, it not only adds your new setInterval()
, is also plays all the old ones along with your new one. That is why I prefer to set and call it only once and use variables to govern whether is should actually execute the code inside each time it fires or it should just return false until one of those variables change. But supposing you cleared it with clearInterval()
how do you get it started again? One key reason for assigning it to a variable is so you can call it again without adding another setInterval()
:
reStartCycle(){
myIntervalVar;
}
Upvotes: 0
Reputation: 7985
The interval variable needs to be tied to the proper scope (this). So where ever you reference the variable "interval" you need to prefix it with "this."
Search the code for "CHANGED" to see where I made changes.
var aFSlides = function(el, options) {
var $slideshows = document.querySelectorAll(el), // a collection of all of the slideshows
$slideshow = {},
Slideshow = {
init: function(el, options) {
this.counter = 0; // to keep track of current slide
this.interval = 0; // to control autoCycle
this.paused = 0; // to keep track of whether paused or not
this.el = el; // current slideshow container
this.$items = el.querySelectorAll('figure'); // a collection of all of the slides, caching for performance
this.numItems = this.$items.length; // total number of slides
options = options || {}; // if options object not passed in, then set to empty object
// options.auto = options.auto || false; // if options.auto object is not passed in, then set to false
this.opts = {
auto: (typeof options.auto === "undefined") ? false : options.auto,
speed: 300, // CHANGED: faster for development
//speed: (typeof options.speed === "undefined") ? 6000 : options.speed,
pause: (typeof options.pause === "undefined") ? false : options.pause,
pauseOnHover: (typeof options.pauseOnHover === "undefined") ? true : options.pauseOnHover,
};
this.$items[0].classList.add('current'); // add .current class to first figure
if (this.opts.auto) {
this.autoCycle(this.el, this.opts.speed, this.opts.pauseOnHover);
}
if (this.opts.pause) {
this.addPause(this.el);
}
},
showCurrent: function(i) {
// increment or decrement this.counter depending on whether i === 1 or i === -1
if (i > 0) {
this.counter = (this.counter + 1 === this.numItems) ? 0 : this.counter + 1;
} else {
this.counter = (this.counter - 1 < 0) ? this.numItems - 1 : this.counter - 1;
}
// remove .show from whichever element currently has it
// http://stackoverflow.com/a/16053538/2006057
[].forEach.call(this.$items, function(el) {
el.classList.remove('current');
});
// add .show to the one item that's supposed to have it
this.$items[this.counter].classList.add('current');
},
pauseCycle: function(el, speed) {
var that = this;
clearInterval(this.interval); // CHANGED: clearInterval doesn't return anything usefull
el.classList.add('paused');
},
unpauseCycle: function(el, speed) {
var that = this;
// CHANGED x2:
window.clearInterval(this.interval);
this.interval = window.setInterval(function() {
that.showCurrent(1); // increment & show
}, speed);
el.classList.remove('paused');
},
addPause: function(el, speed) {
var spanPause = document.createElement("span"),
docFrag2 = document.createDocumentFragment(),
that = this,
thatSpeed = speed;
spanPause.classList.add('pause');
spanPause.innerHTML = 'Pause';
docFrag2.appendChild(spanPause);
el.appendChild(docFrag2);
togglePause = function(el, speed) {
if (that.paused == 1) {
var speed = that.opts.speed;
that.unpauseCycle(el, speed);
that.paused = 0;
return that.paused;
} else if (that.paused == 0) {
var speed = that.opts.speed;
// CHANGED
clearInterval(that.interval);
//interval = clearInterval(that.interval);
that.pauseCycle(el, speed);
that.paused = 1;
return that.paused;
}
}
el.querySelector('.pause').addEventListener('click', function() {
togglePause(el, speed);
}, false);
},
autoCycle: function(el, speed, pauseOnHover) {
var that = this;
if (that.paused == 0) {
that.unpauseCycle(el, speed);
}
if (pauseOnHover) {
el.addEventListener('mouseover', function() {
if (that.paused == 0) {
that.pauseCycle(el, speed);
}
}, false);
el.addEventListener('mouseout', function() {
if (that.paused == 0) {
that.unpauseCycle(el, speed);
}
}, false);
} // end pauseonhover
} // end autoCycle
}; // end Slideshow object .....
// make instances of Slideshow as needed
[].forEach.call($slideshows, function(el) {
$slideshow = Object.create(Slideshow);
$slideshow.init(el, options);
});
};
/* Init for this example snippet */
var aFS56c641d29d032 = {
auto: true,
speed: 2000,
pause: true,
};
aFSlides('.aFS56c641d29d032', aFS56c641d29d032);
body {
font: 400 10px/1.3 Menlo, Courier, sans-serif;
}
figure {
display: none;
}
figure.current {
display: block;
}
figure pre {
font: 700 24px/1.3 Menlo, Courier, sans-serif;
white-space: pre;
}
span {
background: #f66;
color: #fff;
font: 700 16px/1.3 Menlo, Courier, sans-serif;
padding: 10px 20px;
display: inline-block;
}
<div id="smile-gallery" class="aFS56c641d29d032 ">
<div class="slides">
<figure class="slide" id="bna-1">
<div class="slide-content">
<pre>
________________________
| |
| |
| 0 0 |
| |
| \________/ |
| |
|________________________|
</pre>
</div>
</figure>
<figure class="slide" id="bna-2">
<div class="slide-content">
<pre>
________________________
| |
| |
| o O |
| |
| |
| ______/ |
|________________________|
</pre>
</div>
</figure>
<figure class="slide" id="bna-3">
<div class="slide-content">
<pre>
________________________
| |
| |
| ^ ^ |
| |
| |
| (EEEEE) |
|________________________|
</pre>
</div>
</figure>
<figure class="slide" id="bna-4">
<div class="slide-content">
<pre>
________________________
| |
| |
| | | |
| ____________ |
| \ / |
| \________/ |
|________________________|
</pre>
</div>
</figure>
</div>
<!-- /.slides -->
</div>
<!-- /#smile-gallery -->
<p>
Taken from <a href="https://github.com/leemark/better-simple-slideshow" target="_blank">A Better Simple Slideshow</a>.
</p>
Upvotes: 1