This question already has answers here:
How can I pass a parameter to a setTimeout() callback?
(29 answers)
Pass correct "this" context to setTimeout callback?
(6 answers)
Closed 9 years ago.
I am trying to show a div of information on a bar graph if the user hovers over the bar for a second. The answers on this site have gotten me to this point
var timer;
$(".session_hover").on({
'mouseover': function () {
timer = setTimeout(function () {
$(this).children('.session_info').css({'top':175,'right':20}).fadeIn('fast');
}, 1000);
},
'mouseout' : function () {
clearTimeout(timer);
}
});
The above code works when I replace $(this) with $(".session_hover") but then, of course it triggers all the other $(".session_hover") on the page.
How can I pass $(this) into my setTimeout function so that it only applies to the child element of the div I am hovering over?
Thanks for your help!
Try creating a closure around a variable to capture $(this), and then use it in your function:
'mouseover': function () {
var $this = $(this);
timer = setTimeout(function () {
$this.children('.session_info').css({'top':175,'right':20}).fadeIn('fast');
}, 1000);
},
Note that in modern browsers, you can also provide this as a parameter to setTimeout, like this:
'mouseover': function () {
timer = setTimeout(function (t) {
$(t).children('.session_info').css({'top':175,'right':20}).fadeIn('fast');
}, 1000, this);
},
However, if you want this to work in IE < 9, you need to use one of the polyfill techniques described in this MDN article.
Like this:
var timer;
$(".session_hover").on({
var self = this;
'mouseover': function () {
timer = setTimeout(function () {
$(self).children('.session_info').css({'top':175,'right':20}).fadeIn('fast');
}, 1000);
},
'mouseout' : function () {
clearTimeout(timer);
}
});
You need to hold a reference to this outside the setTimeout.
var timer;
$(".session_hover").on({
'mouseover': function () {
var ctx = this;
timer = setTimeout(function () {
$(ctx).children('.session_info').css({'top':175,'right':20}).fadeIn('fast');
}, 1000);
},
'mouseout' : function () {
clearTimeout(timer);
}
});
Another alternative is to use bind which is part of ECMAScript 5 (IE9+).
var timer;
$(".session_hover").on({
'mouseover': function () {
timer = setTimeout((function () {
$(this).children('.session_info').css({'top':175,'right':20}).fadeIn('fast');
}).bind(this), 1000);
},
'mouseout' : function () {
clearTimeout(timer);
}
});
Here's a demo using Bind
Related
This question already has answers here:
Stop setInterval call in JavaScript
(7 answers)
Closed 4 years ago.
documentation states that clearInterval() is required to be passed in a setIntervalId, therefore the function has to look like:
var logMe = setInterval(function () {...}, interval)
Above function is also being self-invoked as soon as the page loads.
If i try to put it in an anonymous function as below:
var logMe = function (interval) {
setInterval(function () {
console.log("whatever");
}, interval);
};
I can pass an interval argument, but I cannot stop it with:
function stopLogMe() {
window.clearInterval(logMe);
};
So the question is, can I create a function "setInterval" that I can pass an argument (interval) and also stop it using clearInterval ?
Define variable and assign timer to it when you're calling logMe function:
var interval = 2000;
var timer = null;
function logMe() {
timer = setInterval(function() {
console.log('hello!');
}, interval);
}
function stopLogMe() {
window.clearInterval(timer);
}
<button onclick="logMe();">Start</button>
<button onclick="stopLogMe();">Stop</button>
You need to somehow encapsulate the ID and the stop function inside a object or function. The ID must be in local context of the logger so it can access it when it needs to stop. It also allows you to create more then just one logger without making things to complex.
const Interval = function (fn, interval) {
this.id = setInterval(fn, interval)
this.clear= function () {
clearInterval(this.id)
}
}
// Create new logger
const myLogger = new Interval(function () {
console.log('Log me')
}, 1000)
// Clear interval after 5 seconds.
setTimeout(myLogger.clear.bind(myLogger), 5000)
I am trying to add the input data into an array delayed for 2 seconds after the last keystroke. However, when I run this I get the following error: Uncaught TypeError: this._validateInput is not a function
How can I properly scope this._validateInput() to run on delay?
I tried let func = this._validateInput();, but that seems to run the function every time that func is set.
Also, the on-change input handler only fires when the input loses focus.
Looking for away to solve this...
<paper-input id="itemId" on-input="_autoAddToArray"></paper-input>
...
_validateInput () {
console.log('validate input');
}
_autoAddToArray () {
let timeout = null;
clearTimeout(timeout);
timeout = setTimeout(function () {
this._validateInput();
}, 2000);
}
Either use a lambda:
setTimeout(
() => this._validateInput(),
2000
);
or bind the function
setTimeout(this._validateInput.bind(this), 2000);
Either solution should work
The lambda works because it doesn't have its own scope.
The binding works because it applies the scope "before" running it so to speak
The this keyword always refers to the this of the current scope, which changes any time you wrap something in function() { ... }
You need to assign your this in the outer scope to a variable.
var self = this;
timeout = setTimeout(function () {
self._validateInput();
}, 2000);
Reference: setTimeout scope issue
<paper-input id="itemId" on-input="_autoAddToArray"></paper-input>
...
_autoAddToArray () {
let timeout = null;
clearTimeout(timeout);
let that = this;
timeout = setTimeout(function () {
that._validateInput();
}, 2000);
}
_validateInput () {
if(this.$.itemId.value) {
// do something
}
}
var workViewer = {
container: document.documentElement,
popup: document.querySelector('.avgrund-popup'),
cover: document.querySelector('.avgrund-cover'),
init: function () {
this.addClass(this.container, 'avgrund-ready');
window.avgrund = {
activate: this.activate,
deactivate: this.deactivate,
disableBlur: this.disableBlur
};
},
activateModal: function (state) {
setTimeout(function () {
this.parent.removeClass(popup, 'no-transition'); //this line
this.parent.addClass(this.container, 'avgrund-active'); //this line
}, 0);
},
removeClass: function (element, name) {
element.className = element.className.replace(name, '');
}
};
module.exports = workViewer;
I want to pass this into setTimeout function, whats the right way to do it?
This is my first post, please let me know if i can improve it in any way
There's two major ways. The first is saving a reference to this and using it instead:
var self = this;
setTimeout(function() {
self.parent.removeClass(popup, 'no-transition');
self.parent.addClass(self.container, 'avgrund-active');
}, 0);
The other is to use bind to create a new function with this bound to the given value.
setTimeout(function() {
this.parent.removeClass(popup, 'no-transition');
this.parent.addClass(this.container, 'avgrund-active');
}.bind(this), 0);
If you're running in an environment that supports them, you can also use an arrow function.
setTimeout(() => {
this.parent.removeClass(popup, 'no-transition');
this.parent.addClass(this.container, 'avgrund-active');
}, 0);
You can use Function.prototype.bind(). It creates function which is bounded to the given context:
setTimeout(function () {
this.parent.removeClass(popup, 'no-transition'); //this line
this.parent.addClass(this.container, 'avgrund-active'); //this line
}.bind(this), 0);
In JavaScript, I have an element (which is an input tag).
This code :
element.addEventListener("focus", function () {
this.parentNode.parentNode.style.outline = this.parentNode.parentNode.dataset.ans_outline;
});
When the input is focused, outline is changed immediately.
My question is : how could I delay this event ?
I've tried :
element.addEventListener("focus", function () {
setTimeout(function(node) {
node.parentNode.parentNode.style.outline = node.parentNode.parentNode.dataset.ans_outline;
}(this), 1000)
});
.. But it doesn't work :(
try this:
element.addEventListener("focus", function () {
var node = this;
setTimeout(function() {
node.parentNode.parentNode.style.outline = node.parentNode.parentNode.dataset.ans_outline;
}, 1000)
});
First argument of setTimeout function is function you want to execute (do not call this function directly).
You can store reference to this in node variable and then use it inside your timed out function (see closures)
Remove the reference to the this and give it this way:
element.addEventListener("focus", function () {
$this = this;
setTimeout(function() {
$this.parentNode.parentNode.style.outline = $this.parentNode.parentNode.dataset.ans_outline;
}, 1000)
});
This question already has answers here:
JavaScript setInterval and `this` solution
(9 answers)
Closed 5 months ago.
I'm trying to get my head round this context problem while using prototypal inheritence (which I've not really played with before). I have an AutoScroller object:
function AutoScroller() {
this.timer = null;
}
AutoScroller.prototype = {
stop: function() {
if (this.timer == null) {
return;
}
clearInterval(this.timer);
this.timer = null;
console.log("stop");
},
start: function() {
if (this.timer != null) {
return;
}
this.timer = setInterval(function() { this.move(); }, 3000);
console.log("start");
},
move: function() {
console.log("move");
}
};
On document ready, I initiate everything by doing this:
var scr = new AutoScroller();
$('div.gallery p.stopBtn').bind("click", scr.stop);
$('div.gallery p.startBtn').bind("click", scr.start);
The problems all arise because "this" always refers to 'p.startBtn' and not scr, so when the start function with setInterval is called I'm getting an error "this.move() is not a function".
I know context is a fairly fundamental concept of which I appear to have no idea. Any ideas on how to sort this out?
Change start to this:
start: function() {
if (this.timer != null) {
return;
}
var that = this;
this.timer = setInterval(function() { that.move(); }, 3000);
console.log("start");
}
I finally worked it out... I used a closure in the button click like this:
var scr = new AutoScroller();
$('div.gallery p.startBtn').bind('click', function(x) {
return function() {
x.start();
}
}(scr));
And also implemented the change mentioned by SimpleCoder above.
You can also pass the current object instance in setInterval method, so that it always has the access to 'this'.
Verified on IE11, Chrome, Opera and Firefox.
setInterval(function (objRef) {
objRef.foo();
}, 500, ***this***);