To blur the keyboard on mobile devices, I use an interval with count on the showPicker() function.
showPicker = function () {
var timesRun = 0;
var intervalblur = setInterval(function () {
timesRun += 1;
if (timesRun === 60) {
clearInterval(intervalblur);
}
console.log('fire blur');
editorInstance.blur();
}, 600);
};
When the picker is closed, I want to clear the interval. I did on this function:
hidePicker = function () {
clearInterval(intervalblur);
}
But I am getting this error:
Uncaught ReferenceError: intervalblur is not defined
Any ideas? Thank you
Because it's not defined in a global scope but in showPicker function's scope.
You have 3 options
Define it globally
var intervalblur;
showPicker = function () {
var timesRun = 0;
intervalblur = setInterval(function () {
timesRun += 1;
if (timesRun === 60) {
clearInterval(intervalblur);
}
console.log('fire blur');
editorInstance.blur();
}, 600);
};
clearInterval(intervalblur);
Define it on window
showPicker = function () {
var timesRun = 0;
window.intervalblur = setInterval(function () {
timesRun += 1;
if (timesRun === 60) {
clearInterval(intervalblur);
}
console.log('fire blur');
editorInstance.blur();
}, 600);
};
clearInterval(intervalblur);
showPicker will return the intervalId
showPicker = function () {
var timesRun = 0;
var intervalblur = setInterval(function () {
timesRun += 1;
if (timesRun === 60) {
clearInterval(intervalblur);
}
console.log('fire blur');
editorInstance.blur();
}, 600);
return intervalblur;
};
var intervalId = showPicker();
clearInterval(intervalId);
Personally I'd prefer option 3 because I don't like global variables.
I am working on knockout js.
In that i have a recursive function which executes a function every minute. for that am using a timer every 60 sec it will execute also same will be reflecting in the UI also.
In my case, if i try to assign or initialize a timer value(observable) which is inside a loop, it doesn't reflecting instead of reflecting it is added to the pipeline and that much time loop is running simultaneously.
In that case i want to kill the loop and again want to restart every time i am changing the timer value.
timerInSec=60;
var loop = function () {
if (this.timer() < 1) {
myFunction()
this.timer(this.timerInSec - 1);
setTimeout(loop, 1000);
} else {
this.timer(this.timer() - 1);
setTimeout(loop, 1000);
}
};
loop();
Here is my solution. Please check.
timerInSec = 60;
const Loop = (function () {
let timer = 0;
let timerId = -1;
const myFunction = function () {
console.log('finished');
}
const fnLog = function (tm) {
console.log('current time = ', tm);
}
const fnProc = function () {
timerId = setTimeout(myFunction, 1000 * timer);
}
return {
start: function (tm = 60) {
this.stop();
timer = tm;
fnProc();
},
stop: function () {
if (timerId !== -1) {
clearTimeout(timerId);
timerId = -1;
}
}
}
})();
Loop.start(timerInSec);
setTimeout(() => {
Loop.start(timerInSec);
}, 500);
So I am trying to run a pop up script using timer interval and for whatever reason the timer doesnt count it just fires on load.
idleTime = 0;
$(document).ready(function() {
$limit = 5;
if ($.cookie('newVisit') != '1') {
$.get('/pop_form.htm', function(data) {
$('.subs-popup').html(data);
});
function timerIncrement() {
idleTime++;
if (idleTime > $limit) {
$('.subs-popup ').show();
idleTime = 0;
}
}
var idleInterval = setInterval(timerIncrement, 1000); // 1 second
$(this).mousemove(function(e) {
idleTime = 0;
});
$(this).keypress(function(e) {
idleTime = 0;
});
$.cookie('newVisit', '1', {
expires: 364,
path: "/"
});
}
});
Just not really sure what else to do in order to get this to work correctly? Any help would be greatly appreciated.
Update***
So I did what you said and here is my code with the changes. Except now i am getting an unexpected token error.
var idleTime = 0;
$(document).ready(function() {
var limit = 7;
if ($.cookie('newVisit') !='1') {
$.get('/pop_form.htm', function(data) {
$('.subs-popup').html(data);
});
function timerIncrement() {
idleTime++;
if (idleTime > limit) {
$('.subs-popup ').show();
idleTime = 0;
}
}
var idleInterval = setInterval(timerIncrement, 1000); // 1 second
$(this).mousemove(function (e) {
idleTime = 0;
});
$(this).keypress(function (e) {
idleTime = 0;
});
$.cookie('newVisit', '1', { expires: 364 , path: "/" });
}
}); <----- this is where i get the unexpected token error
Here's an alternative solution:
var timeout = null;
function resetTimer() {
clearTimeout(timeout)
timeout = setTimeout(function() {
clearTimeout(timeout)
alert("Idle!")
}, 5000);
}
$(document).mousemove(function(e) {
resetTimer();
});
$(document).keypress(function(e) {
resetTimer()
});
This uses the return value from setTimeout() which is a reference to the timeout. You can call clearTimeout() with this reference and it will cancel/clear/remove the timeout, so you can start a new one each time the mouse moves / a key is pressed - rather than keep a record of how many times it's been hit.
Fiddle: https://jsfiddle.net/2ewzr4sx/
Works for me.
Reduced test case:
var idleTime = 0;
$(document).ready(function() {
var limit = 5;
function timerIncrement() {
idleTime++;
if (idleTime > limit) {
alert('Idle!');
idleTime = 0;
}
}
var idleInterval = setInterval(timerIncrement, 1000); // 1 second
$(this).mousemove(function(e) {
idleTime = 0;
});
$(this).keypress(function(e) {
idleTime = 0;
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
PS:
Make sure to declare all your variables with var.
You also should not let your variable begin with $ if they don't hold a jQuery-Object (e.g. $limit should be limit)
Note that
touch-device users wont move any mouse, thus "idle" every time.
Be aware that the interval set with setInterval() ist not guaranteed to be executed in exactly this time. E.g. browsers with throttle intervals when tab is in background.
After first run you won't see any popup this is because of the scope of the setInterval() when it runs 2nd time it runs function in window scope same you can see by
function timerIncrement() {
console.log('scope is:', this);
idleTime++;
/* rest your function code goes here... */
So to prevent it you have to define timerIncrement() in global scope so that setInterval can access and call it; :)
Updated code:
function timerIncrement() {
idleTime++;
if (idleTime > $limit) {
$('.subs-popup ').show();
idleTime = 0;
}
}
var idleTime = 0;
var idleInterval = 0;
var $limit = 5;
$(document).ready(function() {
if ($.cookie('newVisit') != '1') {
$.get('/pop_form.htm', function(data) {
$('.subs-popup').html(data);
});
idleInterval = setInterval(timerIncrement, 1000); // 1 second
$(this).mousemove(function(e) {
idleTime = 0;
});
$(this).keypress(function(e) {
idleTime = 0;
});
$.cookie('newVisit', '1', {
expires: 364,
path: "/"
});
}
});
So, I've searched for this high and low and maybe I'm just having trouble understanding jQuery's deferred function or I'm completely on the wrong track. So any help would be appreciated folks!
I basically have a custom jQuery function messager that displays a message with a fadeOut and fadeIn.
(function ( $ ) {
$.fn.messager = function(message, effect, speed) {
$(this).fadeOut(speed).delay(speed).text(message).fadeIn(speed);
return this;
};
}( jQuery ));
So, I have a div called $elem and when $elem.messager gets called multiple times (with different messages), I would like the messager function to wait till its last call has finished. As in the last FadeIn has finished. Because currently what's happening is that the second call of the function is overwriting the animation effect of the first call of the function.
Any ideas?
jQuery Deferred Way
jQuery Deferred object (roughly compromising CommonJS Promises API) can help us managing queued operations. Here is my implementation of queuing messages. You can pass through multiple messages as an array in one call, or synchronize different message boards easily because #messager() returns jQuery object itself but also wrapped as a promise object which will be resolved just when message(s) being displayed.
(function ($) {
function awaits($el) {
var awaits = $el.data('_awaits');
awaits || $el.data('_awaits', awaits = []);
return awaits;
}
function resolveNext(curr /*, ignored */) {
var head = awaits(this).shift();
if (head === curr) {
resolveNext.call(this, 'not await');
} else {
head && head.resolve();
}
}
function display(message, speed) {
var $self = this, await = $.Deferred(), willDone = $.Deferred();
awaits($self).push(await) > 1 || await.resolve();
await.done(function() {
function reveal() {
$self.text(message).fadeIn(speed, function() {
resolveNext.call($self, await);
willDone.resolve();
});
}
$self.fadeOut(speed/2, reveal);
});
return willDone.promise(this);
};
$.fn.messager = function(message, speed) {
speed = speed || 500;
if ($.isArray(message)) {
var arr = [];
message.forEach(function(m) {
arr.push(display.call(this, m, speed));
}, this);
return $.when.apply(this, arr);
} else {
return display.call(this, message, speed);
}
}
}( jQuery ));
function play() {
$('#msgbox1').messager(['A demo of', 'queued messages'], 1000);
for (var i = 3; i > 0; i--) $('#msgbox1').messager(i);
$('#msgbox1').messager(['Ready to sing...', 'Singing...']);
for (var i = 8; i > 0; i--) $('#msgbox2').messager('***');
for (i = 1; i < 8; i++) $('#msgbox2').messager(String.fromCharCode(64 + i));
$('#msgbox2')
.messager('')
.done(function() {
$('#msgbox1')
.messager(['End of demo.', 'Thank you.', 'Run again?'], 1000)
.done(function() {
$('#msgbox1, #msgbox2').one('click', play);
$('#msgbox2').messager('>');
});
});
}
play();
html {
background: rgba(0, 0, 0, 0.25);
}
#msgbox1, #msgbox2 {
color: #FFF;
padding: 0.3em 0.5em;
font-size: 36pt;
text-align: center;
height: 1.8em;
cursor: default;
}
#msgbox2 {
color: yellow;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Queuing Messages with jQuery Deferred Object</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
<body>
<div id="msgbox1"></div>
<div id="msgbox2"></div>
</body>
</html>
Edit, Updated
Try
(function ($) {
$.fn.messager = messager;
function messager(message, speed, callback) {
var that = $(this);
if (that.data("queue") === undefined) {
$.fx.interval = 0;
that.data("queue", []);
that.data("msg", []);
};
var q = that.data("queue"),
msgs = that.data("msg");
q.push([message, speed, callback]);
msgs.push(message);
var fn = function (m, s, cb) {
return that.fadeOut(s, function () {
that.text(m)
})
.delay(s)
.fadeIn(s, cb)
.promise("fx")
.done(function (el) {
console.log("callback", q.length);
if (q.length > 1) {
q.splice(0, 1);
fn.apply(el, q[0])
} else {
el.data("queue", []);
console.log("done", el.data("queue").length);
always(promise, ["complete", msgs])
.then(complete);
}
return el.promise("fx");
})
}
, promise = $.when(!that.queue("fx").length
? fn.apply(that, q[0])
: that.promise("fx"))
, always = function (elem, args) {
if (elem.state() === "pending") {
console.log(elem.state(), args)
} else {
if (elem.state() === "resolved") {
elem.done(function (elem) {
console.log(msgs.length + " messages complete");
})
};
};
return elem.promise("fx")
};
always(promise, ["start", message, q.length]);
return that
};
}(jQuery));
See .promise()
(function ($) {
$.fn.messager = messager;
function messager(message, speed, callback) {
var that = $(this);
if (that.data("queue") === undefined) {
$.fx.interval = 0;
that.data("queue", []);
that.data("msg", []);
};
var q = that.data("queue"),
msgs = that.data("msg");
q.push([message, speed, callback]);
msgs.push(message);
var fn = function (m, s, cb) {
return that.fadeOut(s, function () {
that.text(m)
})
.delay(s)
.fadeIn(s, cb)
.promise("fx")
.done(function (el) {
console.log("callback", q.length);
if (q.length > 1) {
q.splice(0, 1);
fn.apply(el, q[0])
} else {
el.data("queue", []);
console.log("done", el.data("queue").length);
always(promise, ["complete", msgs])
.then(complete);
}
return el.promise("fx");
})
}
, promise = $.when(!that.queue("fx").length
? fn.apply(that, q[0])
: that.promise("fx"))
, always = function (elem, args) {
if (elem.state() === "pending") {
console.log(elem.state(), args)
} else {
if (elem.state() === "resolved") {
elem.done(function (elem) {
console.log(msgs.length + " messages complete");
})
};
};
return elem.promise("fx")
};
always(promise, ["start", message, q.length]);
return that
};
}(jQuery));
var complete = function() {
if (!$("pre").is("*")) {
$("body").append("<pre>" + JSON.stringify($(this).data("msg"), null, 4))
} else {
$("pre")
.text(JSON.stringify($(this).data("msg"), null, 4));
$("label[for=messages]").text("messages updated")
.show(0).delay(350).hide(0)
};
};
var fx = function() {
$(this).css("color", "purple").animate({
fontSize: "72"
}, 100, function() {
$(this).animate({
fontSize: "36"
}, 100, function() {
$(this).css("color", "inherit")
})
})
};
var input = $("input");
var $elem = $("#messages");
$elem.messager("0", 1000)
.messager("1", 100)
.messager("2", 200)
.messager("3", 300)
.messager("4", 400)
.messager("5", 500)
.messager("6", 600)
.messager("7", 700)
.messager("8", 800)
.messager("9", 900);
$.each("abcdefghijklmnopqrstuvwxyz".split(""), function(key, val) {
$elem.messager(val, 200, fx);
});
$("button").on("click", function() {
$elem.messager(input.val().length > 0 ? input.val() : $.now(), 200);
input.val("")
});
#messages {
display:block;
height:38px;
font-size:36px;
position : absolute;
}
label[for=messages] {
color:blue;
}
pre {
position:relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<label for="button">add messages</label> <label for="messages"></label><br>
<input type="text" /><button>click</button>
<br />
<div id="messages">messages</div>
<br><br>
<script>
(function ( $ ) {
$.fn.messager = function(message, effect, speed, gothru) {
if (!$(this).data('message'))
{
$(this).data('message', Array());
}
$(this).data('message').push({messageContent: message, messageEffect: effect, messageSpeed: speed});
if ($(this).data('message').length > 1 && gothru != true)
{
return;
}
$(this).fadeOut(speed).delay(speed).text(message).fadeIn(speed, function(){
$(this).data("message").shift();
if ($(this).data('message').length > 0)
{
var arrMessage = $(this).data('message');
var messageContent = arrMessage[0].messageContent;
var messageEffect= arrMessage[0].messageEffect;
var messageSpeed= arrMessage[0].messageSpeed;
$(this).data("message").shift();
$(this).messager(messageContent , messageEffect, messageSpeed, true);
}
});
return this;
};
}( jQuery ));
</script>
It's good now.
The naive way of doing it recursively:
Make a global variable(boolean), in this case called queue. If queue is false, set it to true and begin executing the code you want to run. When that code finishes, set queue back to false. Otherwise, if queue was true, just recursively call _this.messager() until queue is set back to false, which would mean that the code is finished running.
fadeIn() and fadeOut() can take callbacks as the final argument, so I'm utilizing that here.
HTML:
<div id="messageBox"></div>
javaScript:
(function ( $ ) {
var queue = false;
$.fn.messager = function(message, effect, speed) {
var _this = $(this);
if (!queue) {
queue = true;
_this.fadeOut(speed, function() {
_this.text(message);
_this.fadeIn(speed, function() {
queue = false;
});
});
} else {
_this.messager(message, effect, speed);
}
return this;
};
}( jQuery ));
$('#messageBox').messager('One', 300);
$('#messageBox').messager('Two', 300);
$('#messageBox').messager('Three', 300);
This typically results in:
Uncaught RangeError: Maximum call stack size exceeded
A more advanced example:
Here we create a second variable called counter to keep track of how many times 'messager' is called recursively and doesn't exceed the limit specified in the options. I set a default of 50, which can be overwritten by the options parameter.
In addition, we've separated out the code that you want to run. This could even be multiple functions that call each other, the important bit is making sure that when your code is finished running, you set queue to false rather than returning false and setting queue to the result of the function. Setting it to the result of the function just makes it undefined until the function finishes returning. We want it to remain as true until the code is finished executing.
This example also throttles the recursive calling so that it's only called once every 100 milliseconds, although that too can be overwritten with whatever value you like (in milliseconds) via the options parameter.
HTML:
<div id="messageBox"></div>
javaScript:
(function( $ ) {
var queue = false;
var counter = 0;
$.fn.messager = function(message, effect, speed, options) {
var _S = $.extend({
throttle: 100,
counter: 50
}, options);
var _this = $(this);
counter += 1;
function codeToRun() {
_this.fadeOut(speed, function() {
_this.text(message);
_this.fadeIn(speed, function() {
queue = false;
});
});
}
if (!queue) {
queue = true;
codeToRun();
counter = 0;
} else {
if (counter < _S.counter) {
setTimeout(function() {
_this.messager(message, effect, speed);
}, _S.throttle);
}
}
return this;
};
})( jQuery );
$('#messageBox').messager('One', 300);
$('#messageBox').messager('Two', 300);
$('#messageBox').messager('Three', 300);
For some reason, calling methods on $(this) directly gives me:
[Window, jquery: "1.11.0", constructor: function, selector: "", toArray: function, get: function…]
But storing $(this) in a variable and calling methods on that variable gives me the correct element:
[div#messageBox, selector: "#messageBox", context: document, jquery: "1.11.0", constructor: function, toArray: function…]
I have a timeout loop like this:
var somedata = {
autoRefreshInterval: 300,
autoRefreshInSec: 300,
myTimeout: null,
doRefresh: _doRefresh,
onTimeout: function () {
this.autoRefreshInSec--;
if (this.autoRefreshInSec <= 0) {
this.autoRefreshInSec = this.autoRefreshInterval;
this.doRefresh();
}
this.myTimeout = $timeout(this.onTimeout, 1000);
},
startTimer: function () {
this.autoRefreshInSec = this.autoRefreshInterval;
this.myTimeout = $timeout(this.onTimeout, 1000);
},
stopTimer: function () {
$timeout.cancel(this.myTimeout);
},
}
It appears that the "this" doesn't work inside of onTimeout callback function, while it works fine for startTimer and stopTimer. How to fix it?
UPDATE:
Since this is lost inside the onTimeout based on one of the answers below, I tried to pass it into like this:
onTimeout: function (self) {
self.autoRefreshInSec--;
if (self.autoRefreshInSec <= 0) {
self.autoRefreshInSec = self.autoRefreshInterval; // Set it here so that it does't start refresh right away. It will be reset when refresh is done.
self.doRefresh();
}
self.myTimeout = $timeout(self.onTimeout(self), 1000);
},
startTimer: function () {
this.autoRefreshInSec = this.autoRefreshInterval;
this.myTimeout = $timeout(this.onTimeout(this), 1000);
},
Strangely, when I debug through the code, it seems working. However, once I removed the break points, self.doRefresh() is fired continuously. Why?
UPDATE 2:
Okay, I created a JSFiddle at http://jsfiddle.net/qY86q/1 to illustrate the problem.
Function.prototype.bind()
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
Solution for your case
angular.module('myApp', [])
.service('timerService', function($timeout) {
var _timer = {
autoRefreshInterval: 300,
autoRefreshInSec: 300,
myTimeout: null,
onTimeout: function() {
this.autoRefreshInSec -= 1;
if (this.autoRefreshInSec <= 0) {
this.autoRefreshInSec = this.autoRefreshInterval;
console.log('refreshing');
}
console.log('time: ', this.autoRefreshInSec);
this.myTimeout = $timeout(this.onTimeout.bind(this), 1000);
},
startTimer: function() {
if (this.myTimeout) {
this.stopTimer(this.myTimeout)
}
this.autoRefreshInSec = this.autoRefreshInterval;
this.myTimeout = $timeout(this.onTimeout.bind(this), 1000);
},
stopTimer: $timeout.cancel // see note(1)
};
var context = {
timer: _timer
};
return context;
}).controller('PrefsCtrl', function PrefsCtrl($scope, timerService) {
$scope.timer = timerService.timer;
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="PrefsCtrl">
<button ng-click="timer.startTimer()">Click to Start or Reset Timer</button>
<div>{{timer.autoRefreshInSec}}</div>
</div>
</div>
note(1), this is shorcut for
stopTimer: function(timer) {
$timeout.cancel(timer)
}
This is a javascript binding issue.
Try:
var somedata; // now this can be referenced in the angular.bind call
somedata = {
onTimeout: angular.bind(somedata, function () {
this.autoRefreshInSec--;
if (this.autoRefreshInSec <= 0) {
this.autoRefreshInSec = this.autoRefreshInterval;
this.doRefresh();
}
this.myTimeout = $timeout(this.onTimeout, 1000);
})
}
which will ensure the this at the time that function is called is "bound" to somedata inside the callback.