uninterrupted setInterval function - javascript

I wanted to display some divs on my HTML page with some animations. So I decided to use the setInterval method to increases the opacity of all of my div. I wrote the below code in order to do this (the toggleAll function is run at the beginning)
var tab = ["lien","contact","comp_info","comp_langue","apropos","loisir","etudes","exp_pro","projet"];
function increaseOpacityDiv(id,opacityMax){
console.log("fonction appelée : increaseOpacityDiv, opacity max :"+opacityMax);
if(document.getElementById(id).style.opacity >= opacityMax){
console.log("entré ! ");
clearInterval(boucle);
document.getElementById(id).style.opacity = opacityMax;
debugger;
}else{
recup = parseFloat(document.getElementById(id).style.opacity+0) + 0.01;
document.getElementById(id).style.opacity = recup;
}
}
function decreaseOpacityDiv(id){
console.log("fonction appelée : decreaseOpacityDiv");
if(document.getElementById(id).style.opacity <= 0){
clearInterval(boucle);
document.getElementById(id).style.opacity = 0;
}else{
recup = parseFloat(document.getElementById(id).style.opacity) - 0.01;
document.getElementById(id).style.opacity = recup;
}
}
function toggleAll(){
console.log("fonction appelée : toggleAll");
if(all){
console.log("faire disparaitre");
for (var i=0;i<tab.length;i++){
boucle = setInterval(decreaseOpacityDiv,5,tab[i]);
}
all=0;
}else{
console.log("faire apparaitre");
for (var i=0;i<tab.length;i++){
boucle = setInterval(increaseOpacityDiv,5,tab[i],1);
}
all=1;
}
}
It works, the browser displays correctly the divs. However I can't disappeared them, in the same way as I displayed them. I found out that my toggleAll function made a lot of setInterval and I can't stop all of them, so the increaseOpacityDiv is run like every 5ms. And the clearInterval method can just stop the last loop made by the setInterval, not the others. This is shown by this below :
console log
Do you know a way I can show my divs then stop all the setInterval ?

Related

the function run more times than it should

'use strict'
let turn =1;
let countx = 0;
let counto = 0;
function addx(id){
document.getElementById(id).innerHTML = '<p>x</p>';
turn = 0;
newclick();
countx++;
checkWin();
}
function addo(id){
document.getElementById(id).innerHTML = '<p>o</p>';
turn = 1;
newclick();
counto++;
checkWin();
}
function checkWin(){
if(countx==3||counto==3){
alert('you win');
}
}
function newclick(){
if(turn==1){
document.getElementById('leftTop').addEventListener("click",function (){addx('leftTop')});
document.getElementById('centerTop').addEventListener("click",function (){addx('centerTop')});
document.getElementById('rightTop').addEventListener("click",function (){addx('rightTop')});
document.getElementById('leftCenter').addEventListener("click",function (){addx('leftCenter')});
document.getElementById('centerCenter').addEventListener("click",function (){addx('centerCenter')});
document.getElementById('rightCenter').addEventListener("click",function (){addx('rightCenter')});
document.getElementById('leftBottom').addEventListener("click",function (){addx('leftBottom')});
document.getElementById('centerBottom').addEventListener("click",function (){addx('centerBottom')});
document.getElementById('rightBottom').addEventListener("click",function (){addx('rightBottom')});
}
else{
document.getElementById('leftTop').addEventListener("click",function (){addo('leftTop')});
document.getElementById('centerTop').addEventListener("click",function (){addo('centerTop')});
document.getElementById('rightTop').addEventListener("click",function (){addo('rightTop')});
document.getElementById('leftCenter').addEventListener("click",function (){addo('leftCenter')});
document.getElementById('centerCenter').addEventListener("click",function (){addo('centerCenter')});
document.getElementById('rightCenter').addEventListener("click",function (){addo('rightCenter')});
document.getElementById('leftBottom').addEventListener("click",function (){addo('leftBottom')});
document.getElementById('centerBottom').addEventListener("click",function (){addo('centerBottom')});
document.getElementById('rightBottom').addEventListener("click",function (){addo('rightBottom')});
}
}
newclick();
on the first click - countx = 1; counto=0.
on the second click - countx = 2; counto=1.
on the third click - countx = 4; counto=3.
what i want to get is:
first click countx =1;counto=0.
second click countx=1;counto=1.
third click countx=2;counto1.
what i wanted is every click is to add 1 to each count when it is his turn.
this is tictacteo game, every click is should replace the event listener.
the function of x somehow called 4 times on the third click, what i get "on board" is 2x and 1o but the countx = 4
addEventListener adds a new additional event listener every time it's run. Use just one event listener instead of trying to replace the old one to make this work better. Replacing them is unnecessary.
The turn variable already tracks whose turn it is. So you can use something like the following.
function genericClick(id) {
if (turn == 1) {
// add an x
document.getElementById(id).innerHTML = '<p>x</p>';
turn = 0;
countx++;
} else {
document.getElementById(id).innerHTML = '<p>o</p>';
turn = 1;
counto++;
}
checkWin();
}

Creating a class based JS instead of id

So I have created this javascript that animates a certain place using it's ID.
The problem is that there are many of those on the site and meaning this I'd have to duplicate this function a lot of times just to replace the x in getElementById("x").
So here is the code I fully done by myself:
var popcount = 0;
var opanumber = 1;
var poptimeout;
function pop() {
if (popcount < 10) {
popcount++;
if (opanumber == 1) {
document.getElementById("nav1").style.opacity = 0;
opanumber = 0;
poptimeout = setTimeout("pop()", 50);
}
else {
document.getElementById("nav1").style.opacity = 1;
opanumber = 1;
poptimeout = setTimeout("pop()", 50);
}
}
else {
popcount = 0;
document.getElementById("nav1").style.opacity = 1;
}
}
function stoppop() {
clearTimeout(poptimeout);
popcount = 0;
document.getElementById("nav1").style.opacity = 1;
}
I would gladly appreciate any information on how I could solve this situation and also any tutorials about using classes and "this".
Something like this; rather than hard code a value into a function it is better to pass the value in so you can reuse the function on more than one thing. In this case you can now call startPop and stopPop with the name of a CSS class.
var popTimeout;
function setOpacity(className, value) {
Array.prototype.forEach.call(
document.getElementsByClassName(className),
function(el) {
el.style.opacity = value;
}
);
}
function pop(className, popCount, opaNumber) {
if (popCount < 10) { //Must be even number so you end on opacity = 1
setOpacity(className, opaNumber);
popTimeout = setTimeout(function() {
pop(className, popCount++, 1-opaNumber);
}, 50);
}
}
function startPop(className) {
pop(className, 0, 0);
}
function stopPop(className) {
clearTimeout(popTimeout);
setOpacity(className, 1);
}
In case you are wondering about the 1 - opaNumber; this is a simpler way of switching a value between 1 and 0. As 1-1=0 and 1-0=1.
Well you started out with recognizing where you have the problem and that's already a good thing :)
To make your code a bit more compact, and get as many things as possible out of the local scope, you could check the following implementation.
It is in a sense a small demo, where I tried adding as much comments as possible.
I edited a bit more after realizing you rather want to use classnames instead of id's :) As a result, I am now rather using the document.querySelectorAll that gives you a bit more freedom.
Now you can call the startPop function with any valid selector. If you want to pop purely on ID, you can use:
startPop('#elementId');
or if you want to go for classes
startPop('.className');
The example itself also add's another function, nl trigger, that shows how you can start / stop the functions.
I also opted to rather use the setInterval method instead of the setTimeout method. Both callback a function after a certain amount of milliseconds, however setInterval you only have to call once.
As an extra change, stopPop also now uses the document.querySelectorAll so you have the same freedom in calling it as the startPop function.
I added 2 more optional parameters in the startPop function, namely total and callback.
Total indicates the maximum times you wish to "blink" the element(s), and the callback provides you with a way to get notified when the popping is over (eg: to update potential elements that started the popping)
I changed it a bit more to allow you to use it for hovering over an element by using the this syntax for inline javascript
'use strict';
function getElements( className ) {
// if it is a string, assume it's a selector like #id or .className
// if not, assume it's an element
return typeof className === "string" ? document.querySelectorAll( className ) : [className];
}
function startPop(className, total, callback) {
// get the element once, and asign a value
var elements = getElements( className ),
current = 0;
var interval = setInterval(function() {
var opacity = ++current % 2;
// (increase current and set style to the left over part after dividing by 2)
elements.forEach(function(elem) { elem.style.opacity = opacity } );
// check if the current value is larger than the total or 10 as a fallback
if (current > (total || 10)) {
// stops the current interval
stopPop(interval, className);
// notifies that the popping is finished (if you add a callback function)
callback && callback();
}
}, 50);
// return the interval so it can be saved and removed at a later time
return interval;
}
function stopPop(interval, className) {
// clear the interval
clearInterval(interval);
// set the opacity to 1 just to be sure ;)
getElements( className ).forEach(function(elem) {
elem.style.opacity = 1;
});
}
function trigger(eventSource, className, maximum) {
// get the source of the click event ( the clicked button )
var source = eventSource.target;
// in case the attribute is there
if (!source.getAttribute('current-interval')) {
// start it & save the current interval
source.setAttribute('current-interval', startPop(className, maximum, function() {
// completed popping ( set the text correct and remove the interval )
source.removeAttribute('current-interval');
source.innerText = 'Start ' + source.innerText.split(' ')[1];
}));
// change the text of the button
source.innerText = 'Stop ' + source.innerText.split(' ')[1];
} else {
// stop it
stopPop(source.getAttribute('current-interval'), className);
// remove the current interval
source.removeAttribute('current-interval');
// reset the text of the button
source.innerText = 'Start ' + source.innerText.split(' ')[1];
}
}
<div class="nav1">
test navigation
</div>
<div class="nav2">
Second nav
</div>
<div class="nav1">
second test navigation
</div>
<div class="nav2">
Second second nav
</div>
<a id="navigation-element-1"
onmouseover="this.interval = startPop( this )"
onmouseout="stopPop( this.interval, this )">Hover me to blink</a>
<button type="button" onclick="trigger( event, '.nav1', 100)">
Start nav1
</button>
<button type="button" onclick="trigger( event, '.nav2', 100)">
Start nav2
</button>
If you do want to take it back to using IDs, then you will need to think about popTimeout if you run this on more than one element at a time.
function setOpacity(id, value) {
document.getElementById(id).style.opacity = value;
}
function runPop(id) {
function pop(id, popCount, opaNumber) {
if (popCount < 10) { //Must be even number so you end on opacity = 1
setOpacity(id, opaNumber);
popTimeout = setTimeout(function() {
pop(id, popCount++, 1-opaNumber);
}, 50);
}
}
var popTimeout;
pop(id, 0, 0);
return function() {
clearTimeout(popTimeout);
setOpacity(id, 1);
}
}
var killPop = [];
function startPop(id) {
killPop[id] = runPop(id);
}
function stopPop(id) {
killPop[id]();
}

Multiple instances of Setinterval

I have a little problem with my code.
I have it setup so that by default, a rotating fadeIn fadeOut slider is auto playing, and when a user clicks on a li, it will jump to that 'slide' and pause the slider for x amount of time.
The problem i have is that if the user clicks on multiple li's very fast, then it will run color1() multiple times with will start colorInterval multiple times. This gives a undesired effect.
So what i need help with, is figuring out how to reset my code each time a li is clicked, so whenever ColorClick is clicked, i want to make sure that there are no other instances of colorInterval before i start a new one.
Thanks in advance!
=================================
edit:
I now have another problem, i believe that i fixed the clearInterval problem, but now if you look at var reset, you'll see that it runs color1() each time a li is clicked, which runs multiple intervals, so i need to delete the previous instance of color1() each time it is called, to make sure that it doesnt repeat any code inside multiple times. So when a li is clicked delete any instances of color1()
or
i need that instead of running color1 in var reset, i will go straight to colorInterval instead of running color1() for each li clicked,
so run colorInterval after x amount of time in var reset.
function color1() {
var pass = 0;
var counter = 2;
var animationSpeed = 500;
var $colorContent = '.color-container-1 .color-content-container'
var $li = '.color-container-1 .main-color-container li'
$($li).on('click', function() {
colorClick($(this));
});
function colorClick($this) {
var $getClass = $this.attr("class").split(' ');
var $whichNumber = $getClass[0].substr(-1);
var $Parent = '.color-container-1 ';
pass = 1;
$($colorContent).fadeOut(0);
$($colorContent + '-' + $whichNumber).fadeIn(animationSpeed);
var reset = setTimeout(function() {
clearTimeout(reset);
pass = 0;
color1();
}, 10000);
}
var colorInterval = setInterval(function() {
if (pass > 0) {
clearInterval(colorInterval);
return; //stop here so that it doesn't continue to execute below code
}
$($colorContent).fadeOut(0);
$(($colorContent + '-' + counter)).fadeIn(animationSpeed);
++counter
if (counter === $($colorContent).length + 1) {
counter = 1;
}
}, 7000);
}
You could just clear the interval inside of the click event.
var colorInterval;
$($li).on('click', function() {
clearInterval(colorInterval);
colorClick($(this));
});
//Your other code
colorInterval = setInterval(function() {
//Rest of your code
});
One thing play with is jQuery animations have a pseudo class selector :animated when animation is in progress
if(!$('.your-slide-class:animated').length){
// do next animation
}

Loop images on hover - setInterval issue

My code is running well...
but there is one thing I can't solve:
when I mouseover the image the loop starts well, but on subsequent mouseovers it starts changing faster and faster...
var Image = new Array("//placehold.it/400x180/?text=Welcome",
"//placehold.it/400x180/?text=To",
"//placehold.it/400x180/?text=My",
"//placehold.it/400x180/?text=Web+page",
"//placehold.it/400x180/?text=INPHP");
var Image_Number=0;
var Image_Length= Image.length;
function change_image(num){
Image_Number = Image_Number + num;
if(Image_Number > Image_Length)
Image_Number = 0;
if(Image_Number < Image_Length)
document.slideshow.src = Image[Image_Number];
return false;
Image_Number = Image_Length;
}
function auto () {
setInterval("change_image(1)", 1000);
}
<img src="//placehold.it/400x180/?text=Welcome" name="slideshow" onmouseover="auto()" />
On every mouseover you're reassigning a brand-new-interval™ resulting in multiple functions running at the same time.
name on IMG tag is an obsolete HTML5 attribute - See also img tag # W3.org
"change_image(1)" ...strings inside setInterval or setTimeout tigger eval. Which is bad. The real function name should be used instead: setInterval(functionName, ms)
You're not managing well the argument num
You cannot have code after a return statement
Use the mouseenter event (instead of mouseover)
and many more errors....
Here's a remake:
var images = [
"//placehold.it/400x180/?text=Welcome",
"//placehold.it/400x180/?text=To",
"//placehold.it/400x180/?text=My",
"//placehold.it/400x180/?text=Web+page",
"//placehold.it/400x180/?text=INPHP"
];
var c = 0; // c as Counter ya?
var tot = images.length;
var angryCat = false;
// Preload! Make sure all images are in tha house
for(var i=0; i<tot; i++) {
var im = new Image();
im.src= images[i];
}
function changeImage() {
document.slideshow.src = images[++c%tot];
}
function auto() {
if(angryCat) return; // No more mouse enter
angryCat = true;
setInterval(changeImage, 1000);
}
<img src="//placehold.it/400x180/?text=Welcome" name="slideshow" onmouseenter="auto()" />
The increment and loop is achieved using ++c % tot
The angryCat boolean flag helps to know it the auto() already started (mouse was there!), in that case it will return; (exit) the function preventing the creation of additional overlapping intervals on subsequent mouseenter (which was your main issue).
Additionally, I'd suggest to keep your JS away from HTML, so instead of the JS attribute event
onmouseenter="auto()"
assign an ID to your image id="myimage" and use JS:
document.getElementById("myimage").addEventListener("mouseenter", auto, false);

setInterval() not working after clearInterval() in this Javascript code

I have been ripping my hair off a couple of nights now with this problem:
I'm trying to create an expanding div with JavaScript. Here's the part of the HTML file:
<div id="bbc_div" class="bbc_div" style="display:none; height:200px;">
<input type="button" value="Show BBC" id="bbc_button" onclick="onclickBBC('bbc_div')" />
And here's the magical non-working JavaScript file:
var maxHeight = 100;
var curHeight = 1;
var wait = 5;
var timerID = new Array();
function onclickBBC(obj) {
if (document.getElementById(obj).style.display == "none") {
slideDown(obj);
}
else {
document.getElementById(obj).style.display="none"
}
}
function slideDown(obj) {
document.getElementById(obj).style.height="1px";
document.getElementById(obj).style.display="block";
timerID[obj] = setInterval("slideDownExec(\"" + obj + "\")", wait);
return;
}
function slideDownExec(obj) {
if (curHeight <= maxHeight) {
curHeight++;
document.getElementById(obj).style.height=curHeight + "px";
}
else {
endSlide(obj);
}
return;
}
function endSlide(obj) {
clearInterval(timerID[obj]);
return;
}
When I reload the page, div expands once to its right height. But, if I push the button without reloading page again after it has hided again, it doesn't work. display:block; works, but setInterval() isn't starting. So this happens after clearInterval() has executed. Why is clearInterval() killing my setInterval() permanently?
The timer is running, you just need to reset a variable:
function slideDown(obj)
{
document.getElementById(obj).style.height = "1px";
curHeight = 1;
I would use jQuery for this, it's a LOT easier.
You have an issue with curHeight not being set to 1 at the top of slideDown. If this doesn't happen, the if statement at the top of slideDownExec will only work the first time.
Additionally, does JavaScript allow non-integer array indexes?
> a['i'] = 4
4
> a
[]
> a['i']
4
What you're actually doing is adding a property called i to the array object. You might as well use an empty object rather than an array.

Categories

Resources