How can I pause a recursive setTimeout while I change tab(focus) - javascript

My Code:
let isBlack = true;
function animate() {
$(".someDiv").animate({
backgroundColor: isBlack ? "transparent" : 'black'
}, 20);
isBlack = !isBlack;
}
const delay = ms => new Promise(res => setTimeout(res, ms));
async function animateAndWait(msArr) {
for (let i = 0; i < msArr.length; i++) {
animate();
await delay(msArr[i]);
}
}
flashFunction();
async function flashFunction() {
await animateAndWait([300, 50]);
if (myRandomNumberBetween(1, 100) <= 10) {
return delay(3000)
.then(flashFunction);
}
await animateAndWait([300, 400]);
delay(3000)
.then(flashFunction);
}
It makes a div blink a couple of times at a ~3 seconds interval.
My problem occurs when I switch tabs. That Browser pauses the timer while I am on another tab, and then, when I come back to this tab, it blinks all the blinks(rapidly) that were missed in background.
I would like to somehow pause the timer while in background or clear the interval. Some guys have asked the same question but I can't incorporate here the answers that have helped them. Or maybe I just don't know how. This might be well over my head but if someone has some time on their hands I would appreciate their help.
Here are some links from the guys with similar problems:
recursive setTimeout() pause on background
Animations pause when browser tab is not visible

You can use window.onblur/window.onfocus events to destroy/regenerate the Timeout when tab loses/recovers focus.
let myTimeout;
window.onfocus = function() {
console.log('focus')
clearTimeout(myTimeout);
myAction();
};
window.onblur = function(){
console.log('blur')
clearTimeout(myTimeout);
};
function myAction(){
/*
your code ...
*/
console.log('new cycle')
myTimeout = setTimeout( myAction, 1000 );
}
myAction();
If you are looking for a timer capable of being paused/resumed take a look at this tiny library

If some beginner like me wants to know how I combined my code with colxi's answer to my question here it is:
let myTimeout;
let isBlack = true;
window.onfocus = function() {
console.log('focus')
clearTimeout(myTimeout);
myAction();
};
window.onblur = function(){
console.log('blur')
clearTimeout(myTimeout);
};
function animate() {
$(".playerInfoCoatTwo").animate({
backgroundColor: isBlack ? "transparent" : 'black'
}, 20);
isBlack = !isBlack;
}
function myAction(){
const delay = ms => new Promise(res => myTimeout = setTimeout(res, ms)); // some changes here
async function animateAndWait(msArr) {
for (let i = 0; i < msArr.length; i++) {
animate();
await delay(msArr[i]);
}
}
flashFunction();
async function flashFunction() {
await animateAndWait([300, 50]);
if (myRandomNumberBetween(1, 100) <= 10) {
return delay(3000)
.then(flashFunction);
}
await animateAndWait([300, 400]);
delay(3000)
.then(flashFunction);
}
console.log('new cycle')
}
myAction();
However I have no idea if it is optimal or not. Nonetheless it works!

Related

Can't use variable as setInterval delay? [duplicate]

I have written a javascript function that uses setInterval to manipulate a string every tenth of a second for a certain number of iterations.
function timer() {
var section = document.getElementById('txt').value;
var len = section.length;
var rands = new Array();
for (i=0; i<len; i++) {
rands.push(Math.floor(Math.random()*len));
};
var counter = 0
var interval = setInterval(function() {
var letters = section.split('');
for (j=0; j < len; j++) {
if (counter < rands[j]) {
letters[j] = Math.floor(Math.random()*9);
};
};
document.getElementById('txt').value = letters.join('');
counter++
if (counter > rands.max()) {
clearInterval(interval);
}
}, 100);
};
Instead of having the interval set at a specific number, I would like to update it every time it runs, based on a counter. So instead of:
var interval = setInterval(function() { ... }, 100);
It would be something like:
var interval = setInterval(function() { ... }, 10*counter);
Unfortunately, that did not work. It seemed like "10*counter" equals 0.
So, how can I adjust the interval every time the anonymous function runs?
You could use an anonymous function:
var counter = 10;
var myFunction = function(){
clearInterval(interval);
counter *= 10;
interval = setInterval(myFunction, counter);
}
var interval = setInterval(myFunction, counter);
UPDATE: As suggested by A. Wolff, use setTimeout to avoid the need for clearInterval.
var counter = 10;
var myFunction = function() {
counter *= 10;
setTimeout(myFunction, counter);
}
setTimeout(myFunction, counter);
Use setTimeout() instead. The callback would then be responsible for firing the next timeout, at which point you can increase or otherwise manipulate the timing.
EDIT
Here's a generic function you can use to apply a "decelerating" timeout for ANY function call.
function setDeceleratingTimeout(callback, factor, times)
{
var internalCallback = function(tick, counter) {
return function() {
if (--tick >= 0) {
window.setTimeout(internalCallback, ++counter * factor);
callback();
}
}
}(times, 0);
window.setTimeout(internalCallback, factor);
};
// console.log() requires firebug
setDeceleratingTimeout(function(){ console.log('hi'); }, 10, 10);
setDeceleratingTimeout(function(){ console.log('bye'); }, 100, 10);
I like this question - inspired a little timer object in me:
window.setVariableInterval = function(callbackFunc, timing) {
var variableInterval = {
interval: timing,
callback: callbackFunc,
stopped: false,
runLoop: function() {
if (variableInterval.stopped) return;
var result = variableInterval.callback.call(variableInterval);
if (typeof result == 'number')
{
if (result === 0) return;
variableInterval.interval = result;
}
variableInterval.loop();
},
stop: function() {
this.stopped = true;
window.clearTimeout(this.timeout);
},
start: function() {
this.stopped = false;
return this.loop();
},
loop: function() {
this.timeout = window.setTimeout(this.runLoop, this.interval);
return this;
}
};
return variableInterval.start();
};
Example use
var vi = setVariableInterval(function() {
// this is the variableInterval - so we can change/get the interval here:
var interval = this.interval;
// print it for the hell of it
console.log(interval);
// we can stop ourselves.
if (interval>4000) this.stop();
// we could return a new interval after doing something
return interval + 100;
}, 100);
// we can change the interval down here too
setTimeout(function() {
vi.interval = 3500;
}, 1000);
// or tell it to start back up in a minute
setTimeout(function() {
vi.interval = 100;
vi.start();
}, 60000);
I had the same question as the original poster, did this as a solution. Not sure how efficient this is ....
let interval = 5000; // initial condition
let run = setInterval(request, interval); // start setInterval as "run"
function request() {
console.log(interval); // firebug or chrome log
clearInterval(run); // stop the setInterval()
// dynamically change the run interval
if (interval > 200) {
interval = interval * .8;
} else {
interval = interval * 1.2;
}
run = setInterval(request, interval); // start the setInterval()
}
This is my way of doing this, i use setTimeout:
var timer = {
running: false,
iv: 5000,
timeout: false,
cb : function(){},
start : function(cb,iv){
var elm = this;
clearInterval(this.timeout);
this.running = true;
if(cb) this.cb = cb;
if(iv) this.iv = iv;
this.timeout = setTimeout(function(){elm.execute(elm)}, this.iv);
},
execute : function(e){
if(!e.running) return false;
e.cb();
e.start();
},
stop : function(){
this.running = false;
},
set_interval : function(iv){
clearInterval(this.timeout);
this.start(false, iv);
}
};
Usage:
timer.start(function(){
console.debug('go');
}, 2000);
timer.set_interval(500);
timer.stop();
A much simpler way would be to have an if statement in the refreshed function and a control to execute your command at regular time intervals . In the following example, I run an alert every 2 seconds and the interval (intrv) can be changed dynamically...
var i=1;
var intrv=2; // << control this variable
var refreshId = setInterval(function() {
if(!(i%intrv)) {
alert('run!');
}
i++;
}, 1000);
This can be initiated however you want. timeout is the method i used to keep it on the top of the hour.
I had the need for every hour to begin a code block on the hour. So this would start at server startup and run the interval hourly. Basicaly the initial run is to begin the interval within the same minute. So in a second from init, run immediately then on every 5 seconds.
var interval = 1000;
var timing =function(){
var timer = setInterval(function(){
console.log(interval);
if(interval == 1000){ /*interval you dont want anymore or increment/decrement */
interval = 3600000; /* Increment you do want for timer */
clearInterval(timer);
timing();
}
},interval);
}
timing();
Alternately if you wanted to just have something happen at start and then forever at a specific interval you could just call it at the same time as the setInterval. For example:
var this = function(){
//do
}
setInterval(function(){
this()
},3600000)
this()
Here we have this run the first time and then every hour.
I couldn't synchronize and change the speed my setIntervals too and I was about to post a question. But I think I've found a way. It should certainly be improved because I'm a beginner. So, I'd gladly read your comments/remarks about this.
<body onload="foo()">
<div id="count1">0</div>
<div id="count2">2nd counter is stopped</div>
<button onclick="speed0()">pause</button>
<button onclick="speedx(1)">normal speed</button>
<button onclick="speedx(2)">speed x2</button>
<button onclick="speedx(4)">speed x4</button>
<button onclick="startTimer2()">Start second timer</button>
</body>
<script>
var count1 = 0,
count2 = 0,
greenlight = new Boolean(0), //blocks 2nd counter
speed = 1000, //1second
countingSpeed;
function foo(){
countingSpeed = setInterval(function(){
counter1();
counter2();
},speed);
}
function counter1(){
count1++;
document.getElementById("count1").innerHTML=count1;
}
function counter2(){
if (greenlight != false) {
count2++;
document.getElementById("count2").innerHTML=count2;
}
}
function startTimer2(){
//while the button hasn't been clicked, greenlight boolean is false
//thus, the 2nd timer is blocked
greenlight = true;
counter2();
//counter2() is greenlighted
}
//these functions modify the speed of the counters
function speed0(){
clearInterval(countingSpeed);
}
function speedx(a){
clearInterval(countingSpeed);
speed=1000/a;
foo();
}
</script>
If you want the counters to begin to increase once the page is loaded, put counter1() and counter2() in foo() before countingSpeed is called. Otherwise, it takes speed milliseconds before execution.
EDIT : Shorter answer.
(function variableInterval() {
//whatever needs to be done
interval *= 2; //deal with your interval
setTimeout(variableInterval, interval);
//whatever needs to be done
})();
can't get any shorter
Here is yet another way to create a decelerating/accelerating interval timer. The interval gets multiplied by a factor until a total time is exceeded.
function setChangingInterval(callback, startInterval, factor, totalTime) {
let remainingTime = totalTime;
let interval = startInterval;
const internalTimer = () => {
remainingTime -= interval ;
interval *= factor;
if (remainingTime >= 0) {
setTimeout(internalTimer, interval);
callback();
}
};
internalTimer();
}
Make new function:
// set Time interval
$("3000,18000").Multitimeout();
jQuery.fn.extend({
Multitimeout: function () {
var res = this.selector.split(",");
$.each(res, function (index, val) { setTimeout(function () {
//...Call function
temp();
}, val); });
return true;
}
});
function temp()
{
alert();
}
This piece of code below accelerates (acceleration > 1) or decelerates (acceleration <1) a setInterval function :
function accelerate(yourfunction, timer, refresh, acceleration) {
var new_timer = timer / acceleration;
var refresh_init = refresh;//save this user defined value
if (refresh < new_timer ){//avoid reseting the interval before it has produced anything.
refresh = new_timer + 1 ;
};
var lastInter = setInterval(yourfunction, new_timer);
console.log("timer:", new_timer);
function stopLastInter() {
clearInterval(lastInter);
accelerate(yourfunction, new_timer, refresh_init, acceleration);
console.log("refresh:", refresh);
};
setTimeout(stopLastInter, refresh);
}
With :
timer: the setInterval initial value in ms (increasing or decreasing)
refresh: the time before a new value of timer is calculated. This is the step lenght
acceleration: the gap between the old and the next timer value. This is the step height
Inspired by the internal callback above, i made a function to fire a callback at fractions of minutes. If timeout is set to intervals like 6 000, 15 000, 30 000, 60 000 it will continuously adapt the intervals in sync to the exact transition to the next minute of your system clock.
//Interval timer to trigger on even minute intervals
function setIntervalSynced(callback, intervalMs) {
//Calculate time to next modulus timer event
var betterInterval = function () {
var d = new Date();
var millis = (d.getMinutes() * 60 + d.getSeconds()) * 1000 + d.getMilliseconds();
return intervalMs - millis % intervalMs;
};
//Internal callback
var internalCallback = function () {
return function () {
setTimeout(internalCallback, betterInterval());
callback();
}
}();
//Initial call to start internal callback
setTimeout(internalCallback, betterInterval());
};
This is my idea for times when you do not want loops like setInterval to overlap.
You also want to be able to set the loop execution delay and start and stop the loop, instansly on the fly.
I am using a loop_flag variable and a setTimeout function.
I set the main function to async so that you can call other functions in the body by calling await. When the main body of your code is running, the main loop waits and does not repeat itself. (which is not the case with setInterval)
An example of a simple code is:
//#NabiKAZ
document.getElementById("btn_start").addEventListener("click", function() {
console.log("Starting...");
loop_flag = true;
loop_func();
});
document.getElementById("btn_stop").addEventListener("click", function() {
console.log("Stoping...");
loop_flag = false;
});
var n = 0;
var loop_flag = false;
var loop_func = async function() {
if (!loop_flag) {
console.log("STOP.");
return;
}
//body main function inhere
n++;
console.log(n);
////
if (loop_flag) {
setTimeout(loop_func, document.getElementById("inp_delay").value);
} else {
console.log("STOP.");
}
}
<input id="inp_delay" value="1000">
<button id="btn_start">START</button>
<button id="btn_stop">STOP</button>
For a more complete code with a fetch request inside the loop, see here:
https://jsfiddle.net/NabiKAZ/a5hdw2bo/
You can use a variable and change the variable instead.
setInterval(() => function, variable)
You can do this by clearing the interval every iteration, changing the timer value and setting the interval again. Hope it helps ;)
For exemple:
const DOMCounter = document.querySelector(".counter")
let timer = 1000
const changeCounter = () => {
clearInterval(interval)
DOMCounter.innerHTML = timer
timer += 1000
timer == 5000 && timer == 1000
interval = setInterval(changeCounter, timer)
}
let interval = setInterval(changeCounter, timer)
<div class="container">
<p class="counter"></p>
</div>
var counter = 15;
var interval = function() {
setTimeout(function(){
// Write your code here and remove console.log, remember that you need declare yourDynamicValue and give it a value
console.log((new Date()).getTime())
window.counter = yourDynamicValue;
window.interval();
}, counter);
}
// It needs to run just once as init
interval();

setTimeout gets faster and faster - how do I pause it?

I have this script here, and it works great. Once the element scrolls into view, it transforms the element by scrolling it up. But I can't get it to stop once it scrolls out of view, and what's worse, it gets faster and faster every time it scrolls into view! How do I reset the timeout? Clearly I'm using clearTimeout wrong, but I don't know how to fix it.
observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
reviewScroll();
} else {
stopReviewScroll();
//What do I put here to pause reviewScroll?
}
});
});
var reviewscroller = document.querySelector('.ReviewList__reviewCardContainer__1hiY0');
function reviewScroll() {
reviewscroller.style.opacity = 1;
i++;
reviewscroller.style.transform = 'translate3D(0px, -'+i+'px , 0px)';
opac = i * .001;
reviewscroller.style.opacity = 1-opac;
clearTimeout(setTimeout(reviewScroll, 40));
setTimeout(reviewScroll, 40);
if (reviewscroller.style.opacity < -0.25) {
reviewscroller.opacity = 1;
i = 0;
reviewscroller.style.transform = 'translate3D(0px, -'+i+'px , 0px)';
}
}
function stopReviewScroll() {
clearTimeout(reviewScroll);
}
document.querySelectorAll('.ReviewList__reviewCardContainer__1hiY0').forEach(image => { observer.observe(image); });
You should be canceling the old timer and installing a new one, like so:
var timerId = null;
function reviewScroll() {
// ...
if (timerId) {
clearTimeout(timerId);
}
timerId = setTimeout(reviewScroll, 40);
// ...
}

How can I pause and resume a setInterval function with Jquery/JS within a loop? [duplicate]

How do I pause and resume the setInterval() function using Javascript?
For example, maybe I have a stopwatch to tell you the number of seconds that you have been looking at the webpage. There is a 'Pause' and 'Resume' button. The reason why clearInterval() would not work here is because if the user clicks on the 'Pause' button at the 40th second and 800th millisecond, when he clicks on the 'Resume' button, the number of seconds elapsed must increase by 1 after 200 milliseconds. If I use the clearInterval() function on the timer variable (when the pause button is clicked) and then using the setInterval() function on the timer variable again (when the resume button is clicked), the number of seconds elapsed will increase by 1 only after 1000 milliseconds, which destroys the accuracy of the stopwatch.
So how do I do that?
You could use a flag to keep track of the status:
var output = $('h1');
var isPaused = false;
var time = 0;
var t = window.setInterval(function() {
if(!isPaused) {
time++;
output.text("Seconds: " + time);
}
}, 1000);
//with jquery
$('.pause').on('click', function(e) {
e.preventDefault();
isPaused = true;
});
$('.play').on('click', function(e) {
e.preventDefault();
isPaused = false;
});
h1 {
font-family: Helvetica, Verdana, sans-serif;
font-size: 12px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>Seconds: 0</h1>
<button class="play">Play</button>
<button class="pause">Pause</button>
This is just what I would do, I'm not sure if you can actually pause the setInterval.
Note: This system is easy and works pretty well for applications that don't require a high level of precision, but it won't consider the time elapsed in between ticks: if you click pause after half a second and later click play your time will be off by half a second.
You shouldn't measure time in interval function. Instead just save time when timer was started and measure difference when timer was stopped/paused. Use setInterval only to update displayed value. So there is no need to pause timer and you will get best possible accuracy in this way.
While #Jonas Giuro is right when saying that:
You cannot PAUSE the setInterval function, you can either STOP it (clearInterval), or let it run
On the other hand this behavior can be simulated with approach #VitaliyG suggested:
You shouldn't measure time in interval function. Instead just save time when timer was started and measure difference when timer was stopped/paused. Use setInterval only to update displayed value.
var output = $('h1');
var isPaused = false;
var time = new Date();
var offset = 0;
var t = window.setInterval(function() {
if(!isPaused) {
var milisec = offset + (new Date()).getTime() - time.getTime();
output.text(parseInt(milisec / 1000) + "s " + (milisec % 1000));
}
}, 10);
//with jquery
$('.toggle').on('click', function(e) {
e.preventDefault();
isPaused = !isPaused;
if (isPaused) {
offset += (new Date()).getTime() - time.getTime();
} else {
time = new Date();
}
});
h1 {
font-family: Helvetica, Verdana, sans-serif;
font-size: 12px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>Seconds: 0</h1>
<button class="toggle">Toggle</button>
Why not use a simpler approach? Add a class!
Simply add a class that tells the interval not to do anything. For example: on hover.
var i = 0;
this.setInterval(function() {
if(!$('#counter').hasClass('pauseInterval')) { //only run if it hasn't got this class 'pauseInterval'
console.log('Counting...');
$('#counter').html(i++); //just for explaining and showing
} else {
console.log('Stopped counting');
}
}, 500);
/* In this example, I'm adding a class on mouseover and remove it again on mouseleave. You can of course do pretty much whatever you like */
$('#counter').hover(function() { //mouse enter
$(this).addClass('pauseInterval');
},function() { //mouse leave
$(this).removeClass('pauseInterval');
}
);
/* Other example */
$('#pauseInterval').click(function() {
$('#counter').toggleClass('pauseInterval');
});
body {
background-color: #eee;
font-family: Calibri, Arial, sans-serif;
}
#counter {
width: 50%;
background: #ddd;
border: 2px solid #009afd;
border-radius: 5px;
padding: 5px;
text-align: center;
transition: .3s;
margin: 0 auto;
}
#counter.pauseInterval {
border-color: red;
}
<!-- you'll need jQuery for this. If you really want a vanilla version, ask -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p id="counter"> </p>
<button id="pauseInterval">Pause</button></p>
I've been looking for this fast and easy approach for ages, so I'm posting several versions to introduce as many people to it as possible.
i wrote a simple ES6 class that may come handy.
inspired by https://stackoverflow.com/a/58580918/4907364 answer
export class IntervalTimer {
callbackStartTime;
remaining = 0;
paused = false;
timerId = null;
_callback;
_delay;
constructor(callback, delay) {
this._callback = callback;
this._delay = delay;
}
pause() {
if (!this.paused) {
this.clear();
this.remaining = new Date().getTime() - this.callbackStartTime;
this.paused = true;
}
}
resume() {
if (this.paused) {
if (this.remaining) {
setTimeout(() => {
this.run();
this.paused = false;
this.start();
}, this.remaining);
} else {
this.paused = false;
this.start();
}
}
}
clear() {
clearInterval(this.timerId);
}
start() {
this.clear();
this.timerId = setInterval(() => {
this.run();
}, this._delay);
}
run() {
this.callbackStartTime = new Date().getTime();
this._callback();
}
}
usage is pretty straightforward,
const interval = new IntervalTimer(console.log('aaa'), 3000);
interval.start();
interval.pause();
interval.resume();
interval.clear();
My simple way:
function Timer (callback, delay) {
let callbackStartTime
let remaining = 0
this.timerId = null
this.paused = false
this.pause = () => {
this.clear()
remaining -= Date.now() - callbackStartTime
this.paused = true
}
this.resume = () => {
window.setTimeout(this.setTimeout.bind(this), remaining)
this.paused = false
}
this.setTimeout = () => {
this.clear()
this.timerId = window.setInterval(() => {
callbackStartTime = Date.now()
callback()
}, delay)
}
this.clear = () => {
window.clearInterval(this.timerId)
}
this.setTimeout()
}
How to use:
let seconds = 0
const timer = new Timer(() => {
seconds++
console.log('seconds', seconds)
if (seconds === 8) {
timer.clear()
alert('Game over!')
}
}, 1000)
timer.pause()
console.log('isPaused: ', timer.paused)
setTimeout(() => {
timer.resume()
console.log('isPaused: ', timer.paused)
}, 2500)
function Timer (callback, delay) {
let callbackStartTime
let remaining = 0
this.timerId = null
this.paused = false
this.pause = () => {
this.clear()
remaining -= Date.now() - callbackStartTime
this.paused = true
}
this.resume = () => {
window.setTimeout(this.setTimeout.bind(this), remaining)
this.paused = false
}
this.setTimeout = () => {
this.clear()
this.timerId = window.setInterval(() => {
callbackStartTime = Date.now()
callback()
}, delay)
}
this.clear = () => {
window.clearInterval(this.timerId)
}
this.setTimeout()
}
The code is written quickly and did not refactored, raise the rating of my answer if you want me to improve the code and give ES2015 version (classes).
I know this thread is old, but this could be another solution:
var do_this = null;
function y(){
// what you wanna do
}
do_this = setInterval(y, 1000);
function y_start(){
do_this = setInterval(y, 1000);
};
function y_stop(){
do_this = clearInterval(do_this);
};
The following code, provides a precision way to pause resume a timer.
How it works:
When the timer is resumed after a pause, it generates a correction cycle using a single timeout, that will consider the pause offset (exact time when the timer was paused between cycles). After the correction cycle finishes, it schedules the following cycles with a regular setInteval, and continues normally the cycle execution.
This allows to pause/resume the timer, without losing the sync.
Code :
function Timer(_fn_callback_ , _timer_freq_){
let RESUME_CORRECTION_RATE = 2;
let _timer_statusCode_;
let _timer_clockRef_;
let _time_ellapsed_; // will store the total time ellapsed
let _time_pause_; // stores the time when timer is paused
let _time_lastCycle_; // stores the time of the last cycle
let _isCorrectionCycle_;
/**
* execute in each clock cycle
*/
const nextCycle = function(){
// calculate deltaTime
let _time_delta_ = new Date() - _time_lastCycle_;
_time_lastCycle_ = new Date();
_time_ellapsed_ += _time_delta_;
// if its a correction cicle (caused by a pause,
// destroy the temporary timeout and generate a definitive interval
if( _isCorrectionCycle_ ){
clearTimeout( _timer_clockRef_ );
clearInterval( _timer_clockRef_ );
_timer_clockRef_ = setInterval( nextCycle , _timer_freq_ );
_isCorrectionCycle_ = false;
}
// execute callback
_fn_callback_.apply( timer, [ timer ] );
};
// initialize timer
_time_ellapsed_ = 0;
_time_lastCycle_ = new Date();
_timer_statusCode_ = 1;
_timer_clockRef_ = setInterval( nextCycle , _timer_freq_ );
// timer public API
const timer = {
get statusCode(){ return _timer_statusCode_ },
get timestamp(){
let abstime;
if( _timer_statusCode_=== 1 ) abstime = _time_ellapsed_ + ( new Date() - _time_lastCycle_ );
else if( _timer_statusCode_=== 2 ) abstime = _time_ellapsed_ + ( _time_pause_ - _time_lastCycle_ );
return abstime || 0;
},
pause : function(){
if( _timer_statusCode_ !== 1 ) return this;
// stop timers
clearTimeout( _timer_clockRef_ );
clearInterval( _timer_clockRef_ );
// set new status and store current time, it will be used on
// resume to calculate how much time is left for next cycle
// to be triggered
_timer_statusCode_ = 2;
_time_pause_ = new Date();
return this;
},
resume: function(){
if( _timer_statusCode_ !== 2 ) return this;
_timer_statusCode_ = 1;
_isCorrectionCycle_ = true;
const delayEllapsedTime = _time_pause_ - _time_lastCycle_;
_time_lastCycle_ = new Date( new Date() - (_time_pause_ - _time_lastCycle_) );
_timer_clockRef_ = setTimeout( nextCycle , _timer_freq_ - delayEllapsedTime - RESUME_CORRECTION_RATE);
return this;
}
};
return timer;
};
let myTimer = Timer( x=> console.log(x.timestamp), 1000);
<input type="button" onclick="myTimer.pause()" value="pause">
<input type="button" onclick="myTimer.resume()" value="resume">
Code source :
This Timer is a modified and simplified version of advanced-timer, a js library created by myself, with many more functionalities.
The full library and documentation is available in NPM and GITHUB
let time = document.getElementById("time");
let stopButton = document.getElementById("stop");
let timeCount = 0,
currentTimeout;
function play() {
stopButton.hidden = false;
clearInterval(currentTimeout);
currentTimeout = setInterval(() => {
timeCount++;
const min = String(Math.trunc(timeCount / 60)).padStart(2, 0);
const sec = String(Math.trunc(timeCount % 60)).padStart(2, 0);
time.innerHTML = `${min} : ${sec}`;
}, 1000);
}
function pause() {
clearInterval(currentTimeout);
}
function stop() {
stopButton.hidden = true;
pause();
timeCount = 0;
time.innerHTML = `00 : 00`;
}
<div>
<h1 id="time">00 : 00</h1>
<br />
<div>
<button onclick="play()">play</button>
<button onclick="pause()">pause</button>
<button onclick="stop()" id="stop" hidden>Reset</button>
</div>
</div>

Stop line by line javascript animation from looping

I'm currently working with a piece of javascript which fades in a paragraph of text line by line.
Javascript isn't by forte and I'm struggling to stop the animation from looping.
Any help and advice on how to achieve this would be great.
Here is a link to a fiddle https://jsfiddle.net/spittman/7wpqkfhj/5
Thanks!
Javascript
$(document).ready(function() {
function fadeInLine(line){
$('<span />', {
html: line.trim(),
style: "opacity: 0"
})
.appendTo($greeting)
.animate({opacity:1}, 500);
}
function rotateLines(interval){
setTimeout(() => {
if(i >= lines.length-1){
i = 0;
interval = 1500;
setTimeout(()=>{ $greeting.empty(); }, interval)
} else {
fadeInLine(lines[i]);
i++;
interval = 1500;
}
rotateLines(interval);
}, interval);
}
const $greeting = $('div#container p');
const text = $greeting.html();
const lines = text.split("\n").filter((e) => e.replace(/\s+/, ""));
let i = 0;
$greeting.empty();
rotateLines();
});
Html
<div id="container">
<p>
There’s another side to London.<br>
Beyond the jurisdiction of The City.<br>
Undiscovered by outsiders.<br>
Loved by insiders.<br>
An ever-changing place...<br>
That teases our wildside and thrills our artside.<br>
Blurs our workside into our playside.<br>
Where we live on the brightside.<br>
And explore our darkside.<br>
Hidden in plain sight, this is Bankside.<br>
London’s other side.<br>
.
</p>
</div>
Put the scheduled call in the else clause, and delete the greeting.empty() line.
function rotateLines(interval){
setTimeout(() => {
if(i >= lines.length-1){
i = 0;
interval = 1500;
// setTimeout(()=>{ $greeting.empty(); }, interval)
} else {
fadeInLine(lines[i]);
i++;
interval = 1500;
rotateLines(interval);
}
// rotateLines(interval);
}, interval);
}
JSFiddle
The animation stops after the whole text has faded in.
Simply get rid of the if-clause that resets the element and counter.
function rotateLines(interval){
setTimeout(() => {
fadeInLine(lines[i]);
i++;
interval = 1500;
rotateLines(interval);
}, interval);
}
JSFiddle

How to unsubscribe from all the Youtube channels at once?

I have subscribed to more than 300 Youtube channels in past 10 years, and now I have to clean my Youtube, unsubscribing all one by one will take some time, is there a way to unsubscribe all the cannels at once?
Step 1: Go to https://www.youtube.com/feed/channels and scroll to the bottom of the page to populate all items to the screen.
Step 2: Right-click anywhere on the page and click "Inspect Element" (or just "Inspect"), then click "Console", then copy–paste the below script, then hit return.
Step 3:
var i = 0;
var myVar = setInterval(myTimer, 3000);
function myTimer () {
var els = document.getElementById("grid-container").getElementsByClassName("ytd-expanded-shelf-contents-renderer");
if (i < els.length) {
els[i].querySelector("[aria-label^='Unsubscribe from']").click();
setTimeout(function () {
var unSubBtn = document.getElementById("confirm-button").click();
}, 2000);
setTimeout(function () {
els[i].parentNode.removeChild(els[i]);
}, 2000);
}
i++;
console.log(i + " unsubscribed by YOGIE");
console.log(els.length + " remaining");
}
Step 4: Sit back and watch the magic!
Enjoy!!
NOTE: If the script stops somewhere, please refresh the page and follow all four steps again.
Updating the answer provided by everyone else (as the latest update did not work for me):
var i = 0;
var count = document.querySelectorAll("ytd-channel-renderer:not(.ytd-item-section-renderer)").length;
myTimer();
function myTimer () {
if (count == 0) return;
el = document.querySelector('.ytd-subscribe-button-renderer');
el.click();
setTimeout(function () {
var unSubBtn = document.getElementById("confirm-button").click();
i++;
count--;
console.log(i + " unsubscribed");
console.log(count + " remaining");
setTimeout(function () {
el = document.querySelector("ytd-channel-renderer");
el.parentNode.removeChild(el);
myTimer();
}, 250);
}, 250);
}
For me this did the trick.
Youtube Channel Unsubscriber (Works April-2020)
Access the link : https://www.youtube.com/feed/channels
Press F12
Insert the code below in your console
function youtubeUnsubscriber() {
var count = document.querySelectorAll("ytd-channel-renderer:not(.ytd-item-section-renderer)").length;
var randomDelay = 500;
if(count == 0) return false;
function unsubscribeVisible(randomDelay) {
if (count == 0) {
window.scrollTo(0,document.body.scrollHeight);
setTimeout(function() {
youtubeUnsubscriber();
}, 10000)
}
unsubscribeButton = document.querySelector('.ytd-subscribe-button-renderer');
unsubscribeButton.click();
setTimeout(function () {
document.getElementById("confirm-button").click()
count--;
console.log("Remaining: ", count);
setTimeout(function () {
unsubscribedElement = document.querySelector("ytd-channel-renderer");
unsubscribedElement.parentNode.removeChild(unsubscribedElement);
unsubscribeVisible(randomDelay)
}, randomDelay);
}, randomDelay);
}
unsubscribeVisible(randomDelay);
}
youtubeUnsubscriber();
References
https://github.com/vinnyfs89/youtube-unsubscriber
This is a little addition to the best answer:
You can also use jscompress[dot]com to compress the script, then add javascript: at the beginning of the script, and add it to your bookmarks — you can run it from there — just in case you're not comfortable using console or something like that.
Most effective values:
(copy all above this, including the last)
var i = 0;
var myVar = setInterval(myTimer, 200);
function myTimer () {
var els = document.getElementById("grid-container").getElementsByClassName("ytd-expanded-shelf-contents-renderer");
if (i < els.length) {
els[i].querySelector('[aria-label="Unsubscribe from this channel."]').click();
setTimeout(function () {
var unSubBtn = document.getElementById("confirm-button").click();
}, 500);
setTimeout(function () {
els[i].parentNode.removeChild(els[i]);
}, 1000);
}
i++;
console.log(i + " unsubscribed by YOGIE");
console.log(els.length + " remaining");
}
Updating MordorSlave answer. Just did it a moment ago.
Go to https://www.youtube.com/feed/channels and copy/paste the following in the console:
var i = 0;
var myVar = setInterval(myTimer, 200);
function myTimer() {
var els = document.getElementById("contents").getElementsByClassName("ytd-subscribe-button-renderer");
if (i < els.length) {
els[i].querySelector('.ytd-subscribe-button-renderer').click();
setTimeout(function() {
var unSubBtn = document.getElementById("confirm-button").click();
}, 500);
setTimeout(function() {
els[i].parentNode.removeChild(els[i]);
}, 1000);
}
i++;
console.log(i + " unsubscribed");
console.log(els.length + " remaining");
}
https://gist.github.com/itsazzad/c1d86c5db86258ca129554a7b9ed92a7
Use the DELAY const wisely; consider your net speed.
// Go to the following link in your YouTube: https://www.youtube.com/feed/channels
// Scroll the page all the way down until you reach the very last subscribed channel in your list
const DELAY = 100;
const delay = ms => new Promise(res => setTimeout(res, ms));
const list = document.querySelectorAll("#grid-container > ytd-channel-renderer");
for (const sub of list) {
await delay(DELAY);
sub.querySelector("#subscribe-button > ytd-subscribe-button-renderer > paper-button").click();
await delay(DELAY);
document.querySelector("#confirm-button > a").addEventListener('click', async event => {
await delay(DELAY);
console.log(sub.querySelector("#text").innerText);
await delay(DELAY);
});
await delay(DELAY);
document.querySelector("#confirm-button > a").click()
await delay(DELAY);
}
Go to https://www.youtube.com/feed/channels.
Scroll all the way down till you see the last subscribed channel.
Open the javascript console, paste the following code and hit enter
let btns = document.querySelectorAll('paper-button > yt-formatted-string');
for (let i = 0; i < btns.length; i += 1) {
if (btns[i].innerText.toLowerCase() === 'subscribed') {
btns[i].click();
document.getElementById('confirm-button').click();
}
}
Note: For me this method worked on Firefox. Chrome was giving warnings and the page became unresponsive. Worked on Oct 20th 2020 for an account with 956 subscribed channels.
Hmm, wish I'd googled before rolling out my own. I had some fun with async and await with this. The screen does some ugly flashing while it's trying to unsubscribe stuff, but this does the job pretty well.
One prerequisite for this script to catch all channels in one go is to "exhaust" the scroller in the page ie., keep scrolling until you reach the end of your channel list. As others have stated, head on over to YouTube Channels, open the developer console and paste the script that follows.
I've commented in relevant parts, in case this ends up becoming a learning experience for someone ;)
/**
* Youtube bulk unsubsribe fn.
* Wrapping this in an IIFE for browser compatibility.
*/
(async function iife() {
// This is the time delay after which the "unsubscribe" button is "clicked"; Tweak to your liking!
var UNSUBSCRIBE_DELAY_TIME = 2000
/**
* Delay runner. Wraps `setTimeout` so it can be `await`ed on.
* #param {Function} fn
* #param {number} delay
*/
var runAfterDelay = (fn, delay) => new Promise((resolve, reject) => {
setTimeout(() => {
fn()
resolve()
}, delay)
})
// Get the channel list; this can be considered a row in the page.
var channels = Array.from(document.getElementsByTagName(`ytd-channel-renderer`))
console.log(`${channels.length} channels found.`)
var ctr = 0
for (const channel of channels) {
// Get the subsribe button and trigger a "click"
channel.querySelector(`[aria-label^='Unsubscribe from']`).click()
await runAfterDelay(() => {
// Get the dialog container...
document.getElementsByTagName(`yt-confirm-dialog-renderer`)[0]
// and find the confirm button...
.querySelector(`#confirm-button`)
// and "trigger" the click!
.click()
console.log(`Unsubsribed ${ctr + 1}/${channels.length}`)
ctr++
}, UNSUBSCRIBE_DELAY_TIME)
}
})()
If someone is looking for a working solution, the following script worked for me:
Go to https://www.youtube.com/subscription_manager and run
$$('.yt-uix-button-subscribed-branded').forEach(function(el) { el.click(); $$('.overlay-confirmation-unsubscribe-button').forEach(function(el) { el.click(); }); console.log('Bye YouTube'); });
Ref: https://www.reddit.com/r/youtube/comments/ad3jv5/mass_unsubscribe_script/
My solution, most up to date i think, but things always changing...
var unsubBtns = document.querySelectorAll(' div:nth-child(2) > div:nth-child(2) > div:nth-child(2) > ytd-subscribe-button-renderer:nth-child(1) > paper-button:nth-child(1)');
var i = 0;
var interV = setInterval(function(){
unsubBtns[i].click();
i++;
document.querySelector('yt-formatted-string.style-blue-text').click()
}, 1000);
Extending on Jani's answer, this one has a DOM progress counter. Absolutely no jQuery.
(function() {
var i = 0;
var ytdElem = document.querySelector("ytd-page-manager ytd-browse.ytd-page-manager");
ytdElem.innerHTML = '<div style="font: 11pt arial; background: yellow; color: white" id="ytdjsds"></div>' + ytdElem.innerHTML;
var element = document.getElementById("ytdjsds");
var count = document.querySelectorAll("ytd-channel-renderer:not(.ytd-item-section-renderer)").length;
if (count == 0) {
element.innerHTML = "No subscriptions were found on your account.";
return;
}
element.innerHTML = `Deleting subscription 1 of ${count}`;
function myTimer() {
if (count == 0) {
element.innerHTML = `Successfully deleted subscriptions`;
return;
}
element.innerHTML = `Deleting subscription ${i + 1} of ${count}`;
el = document.querySelector('.ytd-subscribe-button-renderer');
el.click();
document.getElementById("confirm-button").style.display = "none";
setTimeout(function() {
var unSubBtn = document.getElementById("confirm-button").click();
i++;
setTimeout(function() {
el = document.querySelector("ytd-channel-renderer");
el.parentNode.removeChild(el);
myTimer();
}, 250);
}, 250);
}
myTimer();
})();
as of October 2021
from this page https://www.youtube.com/feed/channels
var list = $$('yt-formatted-string.ytd-subscribe-button-renderer')
function foo($$) {
if (list.length === 0) return
var el = list.pop()
el.click()
setTimeout(_=>{
var cancel = $$('yt-formatted-string.yt-button-renderer')[1]
cancel.click()
console.log('done')
setTimeout(_=>{foo($$)},100)
}, 100)
}
foo($$)

Categories

Resources