My task:
Run setInterval loop when I hover the current block, for example #main
When I hover on some children element of #main, setInterval has to be paused
After when I leave children element of #main, and return my mouse focus back to #main, setTimeOut should run again. here is screen http://joxi.ru/L215V3qh65weW2
My code:
let num = 0;
var timer = function() { // auto click
{ num >= $(`.the_wrap_graf`).children().length-1 ? num = 0 : num++ }
$(`.year-wrap:eq(${num}) .q`).click()
}
var timerID = null // name of interval
$('.the_wrap_feed').hover(function (ev) { // hover run loop #main
timerID = setInterval(timer, 3000);
}, function (ev) { // mouseleave kill loop
clearInterval(timerID)
})
$(`.q`).mouseenter(function(e) { // kill loop when hover square
clearInterval(timerID)
})
If I add callback to $(.q), it breaks down. How can I do it?
You can't pause an interval timer. You can only cancel it and start a new one.
Re the requirement, I think I'd probably use mouseenter and mouseleave (which you're already doing, using hover) and track whether the cursor is in #main or a child:
var timer = 0;
var timerValue = 0;
var inMain = 0;
var inChild = 0;
function showTimer() {
$("#timer").text(
timer ? "Running: " + timerValue : "Not running"
);
}
function updateTimer() {
if (inMain && !inChild) {
if (!timer) {
timer = setInterval(tick, 100);
}
} else {
if (timer) {
clearInterval(timer);
timer = 0;
}
}
}
function tick() {
++timerValue;
showTimer();
}
showTimer();
$("#main")
.hover(
function() {
++inMain;
updateTimer();
},
function() {
--inMain;
updateTimer();
}
);
$("#main .child")
.hover(
function() {
++inChild;
updateTimer();
},
function() {
--inChild;
updateTimer();
}
);
#main {
border: 1px solid #aaa;
padding: 8px;
}
.child {
border: 1px solid #ddd;
margin: 8px;
}
<div id="timer"></div>
<div>
Not in main
<div id="main">
In main, not in any children
<div class="child">one child</div>
<div class="child">another child</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Related
I am trying to make a variable increase every second. What should I include inside the function autoClicker, so that the variable clicks increase by 1 every second? Also, if there are any more problems in the code, could you point them out to me? Sorry if this question seems basic, I am still quite new to JavaScript.
// The variable we are trying to increase
var clicks = 0;
var upgrade1 = 1;
function getClicks() {
clicks += upgrade1;
document.getElementById("clicks").innerHTML = clicks;
};
function buyAutoClicker() {
if (clicks >= 50) {
clicks -= 50
autoClicker()
} else {
alert = "Sorry, you don't have enough clicks to buy this";
}
}
// The function I will use to increase clicks
function autoClicker() {}
You could create an AutoClicker class that has a start, pause, ad update function. It will be in charge of managing the setInterval id.
Edit: I updated it to include upgrade buttons and the target can now be manually clicked.
const upgrades = [{
cost: 50,
rate: 2
}, {
cost: 100,
rate: 4
}];
const main = () => {
const target = document.querySelector('.auto-clicker');
const span = document.querySelector('.info > span');
const btn = document.querySelector('.btn-toggle');
const clicker = new AutoClicker(target, 1000, (clicks) => {
span.textContent = clicks;
}).start();
initializeUpgrades(clicker, upgrades);
btn.addEventListener('click', (e) => {
e.target.textContent = clicker.isRunning() ? 'Start' : 'Pause';
clicker.toggle();
});
};
const initializeUpgrades = (clicker, upgrades) => {
const upgradeContainer = document.querySelector('.upgrades');
upgrades.forEach(upgrade => {
const btn = document.createElement('button');
btn.textContent = upgrade.cost;
btn.value = upgrade.rate;
btn.addEventListener('click', (e) => {
let cost = parseInt(e.target.textContent, 10);
let value = parseInt(e.target.value, 10);
if (clicker.clicks >= cost) {
clicker.clicks -= cost;
clicker.step = value
} else {
console.log(`Cannot afford the ${value} click upgrade, it costs ${cost} clicks`);
}
});
upgradeContainer.appendChild(btn);
});
};
class AutoClicker {
constructor(target, rate, callback) {
if (typeof target === 'string') {
target = document.querySelector(target);
}
this.target = target;
this.rate = rate;
this.callback = callback;
this.init();
}
init() {
this.step = 1;
this.clicks = 0;
this._loopId = null;
this.target.addEventListener('click', (e) => {
this.update();
});
}
isRunning() {
return this._loopId != null;
}
toggle() {
this.isRunning() ? this.pause() : this.start();
}
update() {
this.clicks += this.step;
if (this.callback) {
this.callback(this.clicks);
}
}
start() {
this.update(); // Update immediately
this._loopId = setInterval(() => this.update(), this.rate);
return this;
}
pause() {
clearInterval(this._loopId);
this._loopId = null;
return this;
}
}
main();
.wrapper {
width: 10em;
text-align: center;
border: thin solid grey;
padding: 0.5em;
}
.auto-clicker {
width: 4em;
height: 4em;
background: #F00;
border: none;
border-radius: 2em;
}
.auto-clicker:focus {
outline: none;
}
.auto-clicker:hover {
background: #F44;
cursor: pointer;
}
.info {
margin: 1em 0;
}
.upgrades {
display: inline-block;
}
.upgrades button {
margin-right: 0.25em;
}
<div class="wrapper">
<button class="auto-clicker"></button>
<div class="info">Clicks: <span class="clicks"></span></div>
<button class="btn-toggle">Pause</button>
<div class="upgrades"></div>
</div>
// The variable we are trying to increase
var clicks = 0;
var upgrade1 = 1;
function getClicks() {
clicks += upgrade1;
document.getElementById("clicks").innerHTML = clicks;
};
function buyAutoClicker() {
if (clicks >= 50) {
clicks -= 50
autoClicker()
} else {
alert = "Sorry, you don't have enough clicks to buy this";
}
}
// The function I will use to increase clicks
setInterval(function(){ clicks++;console.log(clicks); }, 1000);
Use setInterval to run a function at a specified interval. This will run increaseClicks every 1000 milliseconds (every second):
function increaseClicks() {
clicks++;
}
var interval = setInterval(increaseClicks, 1000);
Use clearInterval to stop running it:
clearInterval(interval);
You can omit var interval = if you don't want to use clearInterval:
setInterval(increaseClicks, 1000);
There might be several things to improve this code
the use of textContent is preferable to innerHTML, it checks if there are no html tags in the text
then using inline functions like ()=>{} are more useful but in this program it does'nt make a difference, where you to use it in object oriented context you could use it several ways
you don't need document.getElementById, you could just use id.
And finaly (this is just à random tip which has nothing to do with much of anything) you may consider branchless programming because ifs are expensive.
Stackoverflow Branchless Programming Benefits
But anyways you should have fun :)
var clicks = 0;
var upgrade1 = 1;
function getClicks() {
clk.textContent = (clicks += upgrade1);
};
function buyAutoClicker() {
if (clicks >= 50) {
clicks -= 50
setInterval(()=>{getClicks();},1000);
} else {
alert("Sorry, you don't have enough clicks to buy this");
}
}
clk.onclick=()=>{getClicks();};
b.onclick=()=>{buyAutoClicker();};
html,body{height:100%;width:100%;margin:0;}
p{height:50px;width:50px;background:red;}
<p id="clk"></p>
<p id="b"></p>
I have a div which is displayed with onclick then which disappear with setTimeout:
css
#light {
position:absolute;
left:40px;
top:45px;
border-left:50px solid transparent;
border-right:50px solid transparent;
border-bottom:20px solid red;
opacity:0;
}
js (don't know if the syntax is correct but it works)
function change() {
var element = document.getElementById("light");
element.style.opacity = "1";
element.style.transitionDelay = "4s", // only the 1stime with onclick
setTimeout(() => {
element.style.opacity = "0";
}, 5000)
}
html
<button onclick="change()">light</button>
<div id="light"></div>
What I would like is that this action repeats then by itself every 2 minutes:
- after 2min, #light is displayed again for 5s (opacity="1")
- then hidden again (opacity="0")and so on, and so every 2 min.I know about the setInterval() method but it's too difficult for me to use it correctly - the script doesn't work at regular interval (every 2 min).ps: I've looked similar questions but all this is beyond my competence (ie, 0).
I'm not sure why you can't use timeouts?
Wouldn't something like this work?
( you can adjust the timers... I didn't want to wait minutes to see the light blink so I set it to few seconds)
let timer;
let started = false;
let delayTimer;
const lightOn = (clicked) => {
// do nothing if clicked for the second time
if (clicked && started) {return;}
const fn = () => {
const element = document.getElementById("light");
element.classList.add('light-on');
timer = setTimeout(lightOff, 1000);
};
if (clicked) {
delayTimer = setTimeout(fn, 3000);
} else {
fn();
}
started = true;
}
const lightOff = () => {
const element = document.getElementById("light");
element.classList.remove('light-on');
timer = setTimeout(lightOn, 2000);
}
const stop = () => {
clearTimeout(timer);
clearTimeout(delayTimer);
timer = undefined;
delayTimer = undefined;
started = false;
}
.light {
background-color: gray;
opacity: 0.1;
width: 2em;
height: 2em;
display: inline-block;
border: 1px solid black;
border-radius: 2em;
}
.light-on {
background-color: yellow;
opacity: 1;
}
<div id="light" class="light"></div>
<button onclick="lightOn(true)" style="display: block;">start</button>
<button onclick="stop()" style="display: block;">stop</button>
Would be really grateful for some advice with this javascript issue I am having with a click event that seems to be doubling every time my slider is closed then reopened.
When you open the slider for the first time and click through the slides you can see in the console the clicks incrementing by 1 every time the 'btn--next' is clicked which is of course correct. When i then close the slider down and re-open it again when the 'btn--next' is clicked the clicks in the console are now incrementing by 2 every click. Close the slider again and re-open and then the 'btn--next' clicks in the console increment by 3 and so on every time the slider is re-loaded.
https://jsfiddle.net/95afhtx8/2/
var loadSlider = document.querySelector('.load__slider');
loadSlider.addEventListener('click', function() {
var slider = document.querySelector('.animal__slider');
var sliderSlide = document.querySelectorAll('.animal__slider__slide');
var nextSlide = document.querySelector('.btn--next');
var previousSlide = document.querySelector('.btn--previous');
var closeSlider = document.querySelector('.animal__slider__close');
var currentSlide = 0;
slider.classList.add('active');
setTimeout(function() {
slider.classList.add('active--show');
startSlide();
}, 100);
//Reset Slider
function resetSlides() {
for (var s = 0; s < sliderSlide.length; s++) {
sliderSlide[s].classList.remove('active--show');
sliderSlide[s].classList.remove('active');
}
}
//Start Slider
function startSlide() {
resetSlides();
sliderSlide[0].classList.add('active');
setTimeout(function() {
sliderSlide[0].classList.add('active--show');
}, 100);
}
//Previous slide
function slidePrevious() {
resetSlides();
sliderSlide[currentSlide - 1].classList.add('active');
setTimeout(function() {
sliderSlide[currentSlide].classList.add('active--show');
}, 100);
currentSlide--;
}
previousSlide.addEventListener('click', function() {
if (currentSlide === 0) {
currentSlide = sliderSlide.length;
}
console.log('click');
slidePrevious();
});
//Next slide
function slideNext() {
resetSlides();
sliderSlide[currentSlide + 1].classList.add('active');
setTimeout(function() {
sliderSlide[currentSlide].classList.add('active--show');
}, 100);
currentSlide++;
}
nextSlide.addEventListener('click', function() {
if (currentSlide === sliderSlide.length - 1) {
currentSlide = -1;
}
console.log('click');
slideNext();
});
closeSlider.addEventListener('click', function() {
slider.classList.remove('active--show');
slider.classList.remove('active');
resetSlides();
});
});
It's because every time you click on your slider toggle:
loadSlider[s].addEventListener('click', function () {
You're re-running code like this, which will add another click handler to the element:
nextSlide.addEventListener('click', function() {
You can add multiple event listeners to any object in the DOM. So you just keep adding more every time the slider opens.
You have three general options here.
Option 1: only set click handlers once
Don't re-add event handlers inside your loadSlider[s].addEventListener('click', function () { function. Do it outside so you aren't re-adding handlers.
Option 2: remove click handlers on close
You can remove the event listeners on close. To do this, you should store a reference to the function you make, so you can explicitly remove it later. You should do this for any handlers you add.
const nextClick = function () {
...
};
nextSlide.addEventListener('click', nextClick);
function resetSlides() {
nextSlide.removeEventListener('click', nextClick);
...
}
This way, when the slider is hidden, the click functionality will be turned off, and re-opening it will add new click handlers and the old ones won't fire because you removed them.
Option 3: Re-create the elements
If you remove an element from the DOM and make a completely new one, the new one won't have stale click handlers on it. This means you'll need to dynamically build your markup with Javascript (using document.createElement), not store it in the HTML page body.
I update your code to work properly (you need to close the anonymous function of the first event listener before you start declaring the others, otherwise you are copying them over and over and therefore the doubling/quadrupling etc...). I would also suggest to move DOM selectors outside of the event listener, they can evaluate only once:
var loadSlider = document.querySelector('.load__slider');
var slider = document.querySelector('.animal__slider');
var sliderSlide = document.querySelectorAll('.animal__slider__slide');
var nextSlide = document.querySelector('.btn--next');
var previousSlide = document.querySelector('.btn--previous');
var closeSlider = document.querySelector('.animal__slider__close');
var currentSlide = 0;
loadSlider.addEventListener('click', function() {
slider.classList.add('active');
setTimeout(function() {
slider.classList.add('active--show');
startSlide();
}, 100);
});
//Reset Slider
function resetSlides() {
for (var s = 0; s < sliderSlide.length; s++) {
sliderSlide[s].classList.remove('active--show');
sliderSlide[s].classList.remove('active');
}
}
//Start Slider
function startSlide() {
resetSlides();
sliderSlide[0].classList.add('active');
setTimeout(function() {
sliderSlide[0].classList.add('active--show');
}, 100);
}
//Previous slide
function slidePrevious() {
resetSlides();
sliderSlide[currentSlide - 1].classList.add('active');
setTimeout(function() {
sliderSlide[currentSlide].classList.add('active--show');
}, 100);
currentSlide--;
}
previousSlide.addEventListener('click', function() {
if (currentSlide === 0) {
currentSlide = sliderSlide.length;
}
console.log('click');
slidePrevious();
});
//Next slide
function slideNext() {
resetSlides();
sliderSlide[currentSlide + 1].classList.add('active');
setTimeout(function() {
sliderSlide[currentSlide].classList.add('active--show');
}, 100);
currentSlide++;
}
nextSlide.addEventListener('click', function() {
if (currentSlide === sliderSlide.length - 1) {
currentSlide = -1;
}
console.log('click');
slideNext();
});
closeSlider.addEventListener('click', function() {
slider.classList.remove('active--show');
slider.classList.remove('active');
resetSlides();
});
.animals {
text-align: center;
position: relative;
width: 80%;
height: 300px;
margin: 0 auto;
background-color: grey;
}
.load__slider {
text-align: center;
}
.animal__slider {
position: absolute;
width: 100%;
height: 100%;
text-align: center;
display: none;
}
.animal__slider.active {
display: block;
}
.animal__slider.active .animal__slider__close {
display: block;
}
.animal__slider.active+.animal__slider__open {
opacity: 0;
}
.animal__slider__slide {
display: none;
position: absolute;
width: 100%;
height: 100%;
}
.animal__slider__slide1 {
background-color: red;
}
.animal__slider__slide2 {
background-color: green;
}
.animal__slider__slide3 {
background-color: yellow;
}
.animal__slider__slide4 {
background-color: blue;
}
.animal__slider__slide.active {
display: block;
}
.btn {
color: black;
position: absolute;
bottom: 5px;
cursor: pointer;
}
.btn--previous {
right: 60px;
}
.btn--next {
right: 30px;
}
.animal__slider__close {
display: none;
position: absolute;
right: 0;
cursor: pointer;
}
.animal__slider__open {
display: block;
cursor: pointer;
}
<section class="animals">
<div class="animal__slider">
Slider
<div class="animal__slider__slide animal__slider__slide1">
slide 1
</div>
<div class="animal__slider__slide animal__slider__slide2">
slide 2
</div>
<div class="animal__slider__slide animal__slider__slide3">
slide 3
</div>
<div class="animal__slider__slide animal__slider__slide4">
slide 4
</div>
<span class="btn btn--previous">previous</span>
<span class="btn btn--next">next</span>
<span class="animal__slider__close">close slider</span>
</div>
<span class="animal__slider__open load__slider">open slider</span>
</section>
In your code, you call nextSlide.addEventListener(...) each time you open the slider, but you never remove that listener. you have to call the function nextSlide.removeEventListener(...) when you close the slider. You also can make sure to call addEventListener only when you open the slider the first time, or even before you open it, as the html element is never destroyed.
To be able to remove the listener, you have to make it accessible in your code when you close the slider. You can't use anonymous functions for this.
EDIT :
An other, simpler solution is to change
nextSlide.addEventListener('click', function(){...});
to:
nextSlide['onclick'] = function() {...};
I have to stop a timer and an animation of a bar which width is decreasing dependently on a time variable and then resume both if pressed a button, so that the bar animation will continue from where it stopped and the same for the timer. How can I do that?
I can execute a function on button press, it's just the stopping and resuming functions that I don't know.
$("#timebar") is the animated bar.
function startTimer() {
timer = setTimeout(function(){
barAnimation();
}, time);
}
function stopTimer() {
$('#timebar').stop();
$('#timebar').css("width","100%");
clearTimeout(timer);
}
function barAnimation() {
$("#timebar").animate({ width: "0%" }, time, "linear");
}
This doesn't have the animation, but it does offer a start/stop and progress indicator.
var time = 0;
var timer = 0;
var running = false;
function startTimer() {
running = true;
timer = setInterval(function() {
barAnimation();
}, 1000);
}
function stopTimer() {
running = false;
clearInterval(timer);
}
function barAnimation() {
time++;
$("#count").text(time);
$("#timebar").prop("value", time);
}
$("#go").on("click", function(evt) {
if (running) {
stopTimer();
} else {
startTimer();
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<button id="go">Go</button>
<progress id="timebar" value="0" max="100"></progress>
<div id="count"></div>
I think something like this is what you're looking for:
var time = 0;
var remaining = 15000;
var interval;
$('#start').click(startTimer);
$('#stop').click(stopTimer);
function startTimer() {
if (!interval) {
interval = setInterval(function(){
if (remaining % 1000 === 0)
$('#time').html(time++);
remaining -= 100;
}, 100); // 100 w/ modulo instead of 1000 for better precision
barAnimation();
}
}
function stopTimer() {
$('#timebar').stop();
clearInterval(interval);
interval = false;
}
function barAnimation() {
$("#timebar").animate({ width: "0%" }, remaining, "linear");
}
#timebar {
background-color: black;
width: 100%;
height: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="start">Start</button>
<button id="stop">Stop</button>
<div id="time">0</div>
<div id="timebar"></div>
I want to attach 2 event handlers to one button to start and stop an animation respectively. I'm trying to use a boolean value to test if the animation is already running or not so the right event handler is triggered. i.e. if the animation is not running the boolean is false so the run animation event handler will trigger. As soon as I try to add either or both of these handlers the code breaks and the animation won't run. I've not tried using 2 event handlers on an element before and have seen little about it online so I'm not sure if I'm going about this the right way.
How can I attach these handlers to start and stop the animation ?
<div id='robotCont'></div>
<button id='animeBtn'>start/stop animation</button>
div#robotCont {
width:125px;
height:160px;
border:1px solid #22e;
background-image:url("images/robot.jpg");
background-repeat: no-repeat;
background-position: 0px 0px;
}
var robotSwitch = false;
document.getElementById('animeBtn').onclick = function() {
if(robotSwitch == false) {
runningRobot();
} else {
stopRobot();
}
}
var decrement = 0;
function runningRobot() {
var robotCont = document.getElementById('robotCont');
if(decrement < -1900) {
decrement = 0;
}
robotCont.style.backgroundPosition = decrement+ 'px 0px';
decrement -= 120;
timer = setTimeout(runningRobot,100);
robotSwitch = true;
}
function stopRobot() {
clearTimeout(timer);
}
what about this:
var animate = false
timer;
document.getElementById('animeBtn').onclick = function() {
animate = !animate;
if(animate) {
runningRobot();
} else {
stopRobot();
}
}
var decrement = 0;
function runningRobot() {
var robotCont = document.getElementById('robotCont');
if(decrement < -1900) {
decrement = 0;
}
robotCont.style.backgroundPosition = decrement+ 'px 0px';
decrement -= 120;
timer = setTimeout(runningRobot,100);
}
function stopRobot() {
clearTimeout(timer);
}