JS Object, how to access objects from within myself - javascript

So, i see no reason why this isn't working but i am at a wall and frustrated. Why can't i call this.myself from within the wyr.message.close function? Maybe my understanding of this is scewed but i was sure this is referring to the object itself, not the function.
this.myself is undefined
Code:
wyr.message = {
myself: $('.message'),
init: function() {
if(this.myself.is(':visible')){
setTimeout(this.close, 5000);
}
},
close: function(){
this.myself.fadeOut(1200,function(){
this.myself.remove();
});
}
}

wyr.message = {
myself: $('.message'),
init: function() {
var self = this;
if(this.myself.is(':visible')){
setTimeout(function(){
self.close();
}, 5000);
}
},
close: function(){
this.myself.fadeOut(1200,function(){
$(this).remove();
});
}
}
The issue is context. Within the callback function passed to fadeOut, this is bound to the element being worked on by jQuery, not to the wyr.message object.
EDIT:
There's also an issue with the init method. SetTimeout will bind the value of this to the global (window) object - so we save a reference to the this we want and use that to invoke the close method.
You could also look into Function.prototype.bind, but it's not supported in older browsers.

First, javascript object literal property expressions are evaluated at the time you create the object instance. myself will be whatever that selector grabs at that time, which is probably nothing. The myself property needs to be a function if you want it to return the value of $('.message') at the time of invocation. As a consequence you'll need to change all uses to function calls as well.
Second, during the execution of the setTimeout callback, this is bound to the window object, so you need to qualify it appropriately:
wyr.message = {
myself: function() { return $('.message'); },
init: function() {
if(this.myself().is(':visible')){
setTimeout(this.close, 5000);
}
},
close: function(){
message.myself().fadeOut(1200,function(){
$(this).remove();
});
}
};
(Note, this will fade out and remove everything matching the selector when the timeout fires.)

Related

AngularJS scope variable can't be set inside of a timeout [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 3 years ago.
I have a jsfiddle for this issue.
https://jsfiddle.net/uvtw5kp1/4/
$scope.Dropdown = {
open: false,
searchValue: "",
timer: null,
hideIt: function() {
this.timer = $timeout(function() {
alert("timeout happened the value will not change");
this.open = false;
}, 50);
},
hideItNotimer: function() {
this.open = false;
},
showIt: function() {
$timeout.cancel(this.timer);
this.open = true;
}
};
When I call Dropdown.hideItNotimer() on ng-mouseout it has no problem, but when I call Dropdown.hideIt() the variable is not set. I have added an alert to ensure the timer is working, and I've tried doing a scope.apply after. What does work is having a scope level function called within the timer:
like this:
$scope.setDropdownHidden = function(){
$scope.Dropdown.open = false;
}
and call that from within the timeout it works, but I want to avoid this if I can.
What am I missing?
In your timeout function, this does not refer to the Dropdown object, it instead probably refers to the window object. In javascript this always refers to whichever object the function was invoked with, not the object in which it was defined. When $timeout invokes your callback function it will do so with something other than your Dropdown object since it doesn't know about that object.
Related: Javascript closures and this
You need to either capture the value of this in the parent function as a closure variable or bind your callback function to the Dropdown object using angular.bind
Timeout has its own scope since it is a closure, so the $scope.open = false is not updating your controller $scope.open variable, you should avoid using timeout for to update scope variables. You should bind the global scope with using bind as -
hideIt: function() {
this.timer = $timeout(function() {
this.open = false;
}.bind(this), 50);
}

Javascript setTimeout in onClick

I have small problem with a submit button.. When it's clicked I want it to perform different actions, but two later than the other one. I have been trying to use a setTimeout from Javascript and place some of the actions I want it to perform in a different function, but then my code doesn't work anymore because it doesn't know what 'this' is anymore. Any help please?
function test(){
$(this).addClass("voted");
$(this).val('lighted up');
}
$(".stem").click(function() {
$(this).parent().parent().parent().find(".red").addClass("filltherm");
setTimeout(test,1500);
});
Store a reference to this in _this and pass that as a parameter to temp.
function test(el) {
$(el).addClass("voted");
$(el).val('lighted up');
}
$(".stem").click(function() {
var _this = this;
$(this).parent().parent().parent().find(".red").addClass("filltherm");
setTimeout(function () {
test(_this);
}), 1500);
});
$(this) will be undefined in function test as $(this) refers to the current element whose event has occurred and your function is separate thing, use a variable to store reference of it.
do like this:
var temp;
$(".stem").click(function() {
temp = $(this);
$(this).parent().parent().parent().find(".red").addClass("filltherm");
setTimeout(test,1500);
});
function test(){
temp.addClass("voted");
temp.val('lighted up');
}
You need to store a variable holding the value of this:
function test($el){
$el.addClass("voted");
$el.val('lighted up');
}
$(".stem").click(function() {
var $el = $(this);
$el.parent().parent().parent().find(".red").addClass("filltherm");
setTimeout(function() {
test($el);
}, 1500);
});
You can use the proxy method to specify what this shold be for the function call:
setTimeout($.proxy(test, this),1500);

Can I put a method as the argument in the setInterval function?

Preety straight forward question, though I can't find the answer anywhere
I tried these two ways:
setInterval(function(){object/*or this*/.method()},500)
and
setInterval('object/*or this*/.method()',500)
setInterval in fact expects a method as the first argument, though there is an alternative syntax where the first argument can be a string of code (not recommended by most)
If you're having issues with that code, it may have to do with the scope of 'this'
setInterval(function(){this.method()},500)
In the above code, 'this' will refer to the closure itself, and wouldn't be the same as 'this.method' occurring outside of that closure. For example, the following would work:
function MyClass() {
this.thingy = 'yep this is a thingy'
}
var myClass = new MyClass()
// Will log 'MyClass yep this is a thingy'
setInterval(function() { console.log('MyClass', myClass.thingy) }, 1000)
Whereas the following will not work (presuming instantiating the object and calling foo()):
function MyOtherClass() {
this.thingy = 'also a thingy'
}
// Will log 'MyOtherClass undefined'
MyOtherClass.prototype.foo = function() {
setInterval(function() { console.log('MyOtherClass', this.thingy) }, 1000)
}
The second example will work if we get around using 'this' within the closure (presuming instantiating the object and calling bar()):
MyOtherClass.prototype.bar = function() {
var that = this
setInterval(function() { console.log('MyOtherClass', that.thingy) }, 1000)
}
Also be sure that setInterval is being passed the name of a function:
setInterval(someFunction, 500)
rather than executing a function as an argument
setInterval(someFunction(), 500)
This last line of code is usually a mistake, unless someFunction() returns a function itself ;)
The difference between your 2 ways for passing a function to setInterval is whether you want to pass your function as refrence of just copy of it. Allow me to explain it by example:
-1 Referring(demo):
var obj = {
testMethod: function () {
console.log('function (testMethod): intial output');
}
}
setInterval(function () {
obj.testMethod()
}, 1000);
obj.testMethod = function () {
console.log('function (testMethod): changed output');
}
when you run this code, the result 'll be execution of the modified version of testMethod. Because here you dont copy the function! Instead, you refer to it. So whenever function implementation is changed, the last modified version is executed.
-2 Copying(demo):
var obj = {
testMethod: function () {
console.log('function (testMethod): intial output');
}
}
setInterval(obj.testMethod, 1000);
obj.testMethod = function () {
console.log('function (testMethod): changed output');
}
Here all you do is you are passing a copy of the last defined version of the function testMethod to setInterval. So whatever changes you do to testMethod, the result of setInterval will not be changed.

Javascript this object and bind

new Something({
events:{
load: (function loopsiloop(){
console.log(this); // firstime this will be Something, but next call what its
setTimeout(function(){
console.log(this);
$.ajax({
url: 'foo.htm',
context: this,
success: function( response ){
// do something with the response
},
error: function(){
// do some error handling. you
// should probably adjust the timeout
// here.
},
complete: function(){
loopsiloop(); // recurse //need to bind this here like loopsiloop().bind(this)
}
});
}.bind(this), 5000);
}),
click: function(){
alert("clicked");
}
}
})
Please go through the code and read comments, here the problem is i need to use this in setTimeOut function, so I am binding this to setTimeOut, but when I am calling function as recursive the value of this will not be same
NB:- I dont want to pass the object to the function and dont want to use setIntervel (http://www.erichynds.com/javascript/a-recursive-settimeout-pattern/)
Your recursive call can be written like this:
complete: function() {
loopsiloop.call(this);
}
to ensure that the context is correctly set the second time around.
It could also be written like this, but it's not recommended as it'll call .bind over and over on each pass:
complete: loopsiloop().bind(this) // NB: no function wrap, just passes the ref
Don't bind and don't use this. Set var someVariable = this; before you call setTimeout and let it remain in scope for the recursion (using it instead of this inside the function).

Get "this" element ID in jQuery live/delegate with timeout

I am trying to get the ID of an element bound with a jQuery delegate() function. I want to pass the element's ID to another function. The ID returned is always "undefined" and I'm not sure why. Here is my code:
$(document).ready(function() {
var timeout = undefined;
$('body').delegate(
'#tab-form input[type="text"]',
'keypress',
function(){
if(timeout != undefined) {
clearTimeout(timeout);
}
timeout = setTimeout(function() {
timeout = undefined;
alert($(this).attr('id'));
}, 500);
}
);
});
And my markup:
<form id="tab-form">
<input type="text" value="" id="Tab_name" name="Tab[name]">
<input type="text" value="" id="Tab_text" name="Tab[text]">
</form>
Making a keypress in the text input pops up a JS alert that says "undefined", instead of "Tab_name" or "Tab_text" like I imagined it would.
My initial Googling around leads me to believe that the reason for the attr('id') being undefined is that "this" is not actually a single DOM element, but is an array of the elements that delegate() is attached to. I have not been able to figure out how to access the current bound element's DOM object in the jQuery object array.
Any help is much appreciated!
It's because this isn't what you want it to be in that anonymous function, it's window. There are a few ways to solve this, for example using $.proxy(), like this:
timeout = setTimeout($.proxy(function() {
timeout = undefined;
alert(this.id);
}, this), 500);
The context is window because window owns setTimeout. Just cache it:
$(document).ready(function() {
var timeout = undefined;
$('body').delegate(
'#tab-form input[type="text"]',
'keypress',
function(){
var el = this;
if(timeout != undefined) {
clearTimeout(timeout);
}
timeout = setTimeout(function() {
timeout = undefined;
alert($(el).attr('id'));
}, 500);
}
);
});
meder already pointed out the reason for the behavior. You might also pass in the event object and use target:
$(document).ready(function() {
var timeout = undefined;
$('body').delegate(
'#tab-form input[type="text"]',
'keypress',
function(event){
if(timeout != undefined) {
clearTimeout(timeout);
}
timeout = setTimeout(function() {
timeout = undefined;
alert($(event.target).attr('id'));
}, 500);
}
);
});
Sidenote: using .delegate() on the document.body does not make sense at all. You could just bind those events with .live() to your elements.
Since the other options are spoken for, I'll give the closure option. :o)
(function( th ) {
timeout = setTimeout(function() {
timeout = undefined;
alert(th.id);
}, 500);
})( this );
EDIT: To explain what is happening, basically you're creating a function, calling the function and passing in this as the argument to the function all at the same time.
Think of it this way:
// Create a new function that accepts one argument
function myFunc( th ) {
timeout = setTimeout(function() {
timeout = undefined;
alert(th.id);
}, 500);
}
// Immediately call the function, and pass "this" in to it
myFunc( this );
This is the exact same thing. Take this, wrap the entire function in (), make it anonymous by removing the name of the function myFunc, and then take the execution operator ( this ) from the function call, and place it directly after the function.
When you do that, you're essentially calling the function just after you've created it. It is just an easy way of immediately executing an unnamed (anonymous) function.

Categories