I'm trying to give my jQuery a bit more a structure with a basic object literal pattern and encountered an issue with calling my custom move function as per the following.
(function() {
var movingMenu = {
menu: $('.nav').children('li'),
move: function() {
menu.each(function() {
$(this).animate({
top: '+=50'
}, 200);
});
}
};
})();
window.setInterval(function(){
movingMenu.move();
}, 1000);
I'm trying to call this function every second but the calling part itself doesn't seem to work. I suspect that the variable movingMenu is maybe outside the scope that within window.setInterval it doesn't have a clue in which object this function belongs to?
Demo is available JSFiddle
The code you post here would not work, as you use an IIFE to wrap the object, the setInterval can't access movingMenu. However, your code in jsfiddle is correct. You can either dewrap the IIFE, or put the setInterval into the IIFE, or return a object that exposed the move function. You just need to ensure that movingMenu, or the move is accessible to setInterval.
Use this to get the ref of that menu in your function, as its an attribute of movingMenu, not an variable.
Altered jsfiddle
Move everything out of IIFE:
var movingMenu = {
menu: $('.nav').children('li'),
move: function () {
// VVVV you need to use this to reference to `movingMenu`, so this.menu is the referenced `li`s.
this.menu.each(function () {
$(this).animate({
top: '+=50'
}, 200);
});
}
};
window.setInterval(function () {
movingMenu.move();
}, 1000);
Move setInterval into IIFE as well:
(function(){
var movingMenu = {
menu: $('.nav').children('li'),
move: function () {
// VVVV you need to use this to reference to `movingMenu`, so this.menu is the referenced `li`s.
this.menu.each(function () {
$(this).animate({
top: '+=50'
}, 200);
});
}
};
window.setInterval(function () {
movingMenu.move();
}, 1000);
})();
Yes, you are right, the variable movingMenu is out of scope.
Also, to use the property menu inside the method you need to use this.menu. There is no object scope in JavaScript, so even if you are "inside" the object, you can't directly access the object properties.
(function() {
var movingMenu = {
menu: $('.nav').children('li'),
move: function() {
// use "this" to access properties
this.menu.each(function() {
$(this).animate({
top: '+=50'
}, 200);
});
}
};
// use the variable inside the scope
window.setInterval(function(){
movingMenu.move();
}, 1000);
})();
You're correct about movingMenu being unavailable. To get around this, you want to set your module to a variable and return whatever you want to access outside of the module.
var movingMenu = (function() {
return {
menu: $('.nav').children('li'),
move: function() {
this.menu.each(function() {
$(this).animate({
top: '+=50'
}, 200);
});
}
};
})();
window.setInterval(function(){
movingMenu.move();
}, 1000);
edit: whoops, I read object literal pattern then saw the module pattern and ran in the wrong direction.
The scope of the movingMenu object is limited to the anonymous function that wraps it... function() { var movingMenu = { ... } }.
You're using a syntax that is allowing you to define that anonymous function, and then invoke or call it immediately.
Declaration: (function() { var movingMenu = {..} })
Invocation: (function() { ... })()
This would be the same as if you said...
var foo = function() { var movingMenu = {...} };
foo();
So, in that context, movingMenu is a variable that is defined inside another function, foo. Nothing oustide of foo knows anything about movingMenu. This is the idea of scope. movingMenu exists in the scope of foo.
So to get the functionality of movingMenu outside the scope of foo, we can return movingMenu from foo. What this does is makes the return value of foo the movingMenu object, like so...
var foo = function() {
var movingMenu = {
menu: $('.nav').children('li'),
move: function() {
menu.each(function() {
$(this).animate({
top: '+=50'
},200);
});
}
};
return movingMenu;
};
var menuHandler = foo(); // save movingMenu, returned from foo()
Then, you can use menuHandler like you would movingMenu.
window.setInterval(function () {
menuHandler.move();
}, 1000);
Since you're declaring the function anonymously (not giving it a name, like I did with foo), and then you're invoking it right away, you want to store the return value right away, since you won't be able to invoke that method again.
var foo = function() {
var movingMenu = {
menu: $('.nav').children('li'),
move: function() {
menu.each(function() {
$(this).animate({
top: '+=50'
},200);
});
}
};
return movingMenu;
};
var menuHandler = foo(); // save movingMenu, returned from foo()
Then, you can use menuHandler like you were trying to use movingMenu. Really, could use the name movingMenu instead of menuHandler, I just think it's less confusing this way.
window.setInterval(function () {
menuHandler.move();
}, 1000);
So putting it altogether, with the anonymous function...
var menuHandler = (function() {
var movingMenu = {
menu: $('.nav').children('li'),
move: function() {
menu.each(function() {
$(this).animate({
top: '+=50'
},200);
});
}
};
return movingMenu;
})();
window.setInterval(function () {
menuHandler.move();
}, 1000);
So why would you do this? Why not just make movingMenu public and invoke it directly, instead of wrapping it in an anonymous method and exposing it as a return value from that method? The reason again has to do with scope. By limiting the scope and controlling what is exposed, you can actually create (somewhat, details are beyond the scope of this question) private properties in js.
For example...
var menuHandler = (function() {
// ADDED NEXT LINE FOR EXAMPLE:
var privateCounter = 0;
var movingMenu = {
menu: $('.nav').children('li'),
move: function() {
menu.each(function() {
$(this).animate({
top: '+=50'
},200);
// ADDED NEXT LINE FOR EXAMPLE:
console.log("Count is now: " + (privateCounter++).toString());
// look at the console tab of browser's dev tools (F-12 on windows)
});
}
};
return movingMenu;
})();
From this example, you can now see that movingMenu is exposed (as menuHandler), and it has the ability to use the private variable privateCounter, however, privateCounter is not exposed. So basically this pattern makes everything private initially, so you can expose just what you want to be public.
var menuHandler = (function() {
var privateCounter = 0,
menu = (function() {
return $('.nav').children('li');
})();
var movingMenu = {
move: function() {
menu.each(function() {
$(this).animate({
top: '+=50'
}, 200);
});
console.log("Count is now: " + (privateCounter++).toString());
}
};
return movingMenu;
})();
setInterval(function() {
menuHandler.move();
}, 1000);
.nav {
position: absolute;
}
.nav li {
position: relative;
top: 0;
}
ul {
list-style: none;
}
ul li {
display: inline-block;
background: red;
border-radius: 50%;
color: white;
width: 50px;
height: 50px;
text-align: center;
line-height: 3;
}
<ul class="nav">
<li>Look</li>
<li>Play</li>
<li>Eat</li>
<li>See</li>
</ul>
NOTE In my snippet I've modified your code to make the menu property non-static. This will handle items being added or removed from the menu.
Related
I couldn't find a solution to this, but the second this after mousedown will not work. I've seen different things about binding it but I haven't had any luck. (It works everywhere else in the code).
$(".rightArrow").click(function () {
var stop_slide = parseInt($(this).prev().css("marginLeft"));
scroll_size = $(this).prev().children().size() * 177 * -1;
if(stop_slide > scroll_size){
var int00; // declared here to make it visible to clearInterval.
$(this).mousedown(function(){
int00 = setInterval(function() {
$(this).prev().css( { marginLeft : "-=1px" } );
}, 1);
}).mouseup(function() {
clearInterval(int00);
});
}
});
The this from the setInterval is different than the this from mousedown. Simply use a self variable, this way:
$(".rightArrow").click(function() {
var stop_slide = parseInt($(this).prev().css("marginLeft"));
scroll_size = $(this).prev().children().size() * 177 * -1;
if (stop_slide > scroll_size) {
var int00; // declared here to make it visible to clearInterval.
$(this).mousedown(function() {
var self = this;
int00 = setInterval(function() {
$(self).prev().css({
marginLeft: "-=1px"
});
}, 1);
}).mouseup(function() {
clearInterval(int00);
});
}
});
Each function declared via the function statement has its own context (this), hence, you need to store the previous context into a variable to access it in another function.
Another way is to use bind:
int00 = setInterval(function () {
$(this).prev().css(...);
}.bind(this));
This will bind the current context (the current this) to the setInterval callback.
And if you use es2015, you can use arrow functions:
// the `this` from here
int00 = setInterval(() => {
// will be the `this` from here
});
The arrow functions have no context––they inherit the current context.
How to access the closure scope variables in inner function in JavaScript.
I want to access UL variable in setTimeout function.
ul.find("li").each(function (a, ele) {
$(ele).attr("tabindex", options.items[a].tabindex);
$(ele).on("focusout", function () {
setTimeout(function () {
**//ACCESS UL HERE**
debugger;
}, 1);
}.bind(ul,ele));
}.bind(ul));
Using closure in setTimeout
Closures 'remember' the environment in which they were created.
ul.find("li").each(function(a, ele) {
$(ele).attr("tabindex", options.items[a].tabindex);
$(ele).on("focusout", function() {
setTimeout(function(ul) {
return function() {
console.log(ul);
}
})(ul), 1);
}.bind(ul, ele));
}.bind(ul));
It works normally. The scope of the ul is valid inside the setTimeout function.
ul.find("li").each(function() {
$(this).attr("tabindex", options.items[a].tabindex)
.on("focusout", function() {
setTimeout(function() {
ul.css('color', 'orange');
}, 1);
});
});
A simple code like this will explain you:
(function s() {
var a = "hi";
setTimeout(function () {
console.log(a);
}, 1000);
})();
Here, the ul is the same as the a variable.
You can use access the UL by finding the parent of current element. For getting the immediate parent with specified type, you can use .closest() method
$(ele).on("focusout", function() {
var el = $(this);
setTimeout(function() {
el.closest("ul");
debugger;
}, 1);
}.bind(ul, ele));
Can anyone please tell my why this doesn't work?
(function() {
window.Test = {};
})();
Test.Timers = {
c: null,
startTimer: function() { c = 0; setTimeout(this.doWork, 0); },
doWork: function() {
c++;
alert(c);
setTimeout(this.doWork, 0);
}
};
When I call Test.Timers.startTimer(), it only alerts once with a 1.
Thanks
A method doesn't "remember" its owner (its this); you can copy a method from one object to another, and treat it like any other function. It only has the right owner when you actually call it using dot-notation, e.g. this.doWork().
So your problem is that you're passing the function this.doWork to setTimeout, and then it gets called as a function without knowing its owner, and suddenly its this is window instead of your timer object. To fix this, you need to keep track of your this yourself. For example, you might write:
Test.Timers = (function () {
var newTimer = {
c: null,
startTimer: function() {
this.c = 0;
setTimeout(function () { newTimer.doWork(); }, 0);
},
doWork: function() {
this.c++;
alert(this.c);
setTimeout(function () { newTimer.doWork(); }, 0);
}
};
return newTimer;
})();
or:
Test.Timers = (function () {
var startTimer = function() {
newTimer.c = 0;
setTimeout(doWork, 0);
};
var doWork = function() {
newTimer.c++;
alert(newTimer.c);
setTimeout(doWork, 0);
};
var newTimer = {
c: null,
startTimer: startTimer,
doWork: doWork
};
return newTimer;
})();
(Note that I also changed c to this.c or newTimer.c where necessary, since your version refers repeatedly to window.c. Also note that in the second version, if you don't need external code to be able to access c, you can change it to a local variable, making things cleaner.)
As per your comment to ruakh's answer, I prefer the following approach, myself:
Test.Timers = (function () {
var this_ = {
c: null,
startTimer: function() { this_.c = 0; setTimeout(this_.doWork, 0); },
doWork: function() {
this_.c++;
alert(this_.c);
setTimeout(this_.doWork, 0);
}
};
return this_;
})();
That way, the meaning is clear as this_ looks like this, and all you have to do is get used to the closure pattern of making an anonymous function and calling it right away. Also note that I fixed your reference to c to refer to this_.c instead of a global variable c.
Alternatively, you can use .bind() to bind the function's this to a particular thing. This is built-in to Chrome's V8, at least, and perhaps Firefox as well:
Test.Timers = {
c: null,
startTimer: function() { this.c = 0; setTimeout(this.doWork, 0); },
doWork: function() {
this.c++;
alert(this.c);
setTimeout(this.doWork, 0);
}
};
Test.Timers.startTimer = Test.Timers.startTimer.bind(Test.Timers);
Test.Timers.doWork = Test.Timers.doWork.bind(Test.Timers);
I have written the following function.
function obj()
{
this.a;
}
obj.prototype.catch = function()
{
alert('Catched')
}
obj.prototype.do = function()
{
alert('called');
}
What i need is, to call obj::catch() after obj::do() is called and the call must be performed from inside obj::do()
So how to pass the local function of obj to setTimeout
i have tried
obj.prototype.do = function()
{
window.setTimeout('"'+this.catch+'()"',1000);
alert('called');
}
It does not worked
Then i tried
obj.prototype.do = function()
{
window.setTimeout('"'+this+'.catch()"',1000);
alert('called');
}
which gave me the following error on Chrome console
Uncaught SyntaxError: Unexpected token ILLEGAL
So i tried the following dirty method(is it really dirty ?)
obj.prototype.do = function()
{
this.pid = randomVal(100);
window['temp'+this.pid] = this;
window.setTimeout("temp"+this.pid+".catch();",1000);
alert('called');
}
function randomVal(bound)//returns a random number between 0 and <bound>
{
return (Math.floor(Math.random()*(bound)));
}
That worked.
so why the first two methods not worked.Is there any other way to do the same thing without global variables..
The second method and last method are almost similar .But why am i gettng the error in second method..?
The worked code can be found here
http://jsfiddle.net/jXhAs/
Don't pass strings to setTimeout … ever.
var self = this; // Because the scope will change
setTimeout(function () { self.catch() },1000);
Or if you are using JS 1.8.5:
setTimeout(this.catch.bind(this),1000);
You can read more about bind
You should pass a function to setTimeout (not a string):
Example:
var self = this;
setTimeout(function(){
self.catch();
},1000);
use a closure
obj.prototype.do = function()
{
window.setTimeout((function(that){
return function(){
that.catch();
};
})(this),1000);
alert('called');
}
Why go through all of this effort, just pass the function.
function obj() {
this.a;
}
obj.prototype.
catch = function() {
alert('Catched')
}
obj.prototype.do = function() {
setTimeout(this.
catch, 1000);
}
var test = new obj();
test.do();
var Test = (function() {
return {
useSub: function () {
this.Sub.sayHi();
},
init: function () {
$(document).ready(this.useSub);
}
};
})();
Test.Sub = (function () {
return {
sayHi: function () {
alert('hi');
}
};
})();
Test.useSub(); // works
Test.init(); // explodes
Above I am trying to create a Test namespace and add an object Sub to it. I was doing fine until I tried using the object in jQuery. The error is "Uncaught TypeError: Cannot call method 'sayHi' of undefined". If there is a better way to do this, I am open to it.
Edit:
Obviously this was demo code. In my real application the solution that I went with because I think it is the most clear is this one:
var Namespace (function () {
return {
init: function () {
$(document).ready(function() {
Namespace.onReady();
}
},
onReady: function() {
alert('Now I am back in the Namespace scope. Proceed as planned');
}
};
})();
Edit2: All jQuery callbacks seem to require they are used in this manner or else the scoping is screwed up.
I think it is a scope problem. If you do
$(document).ready(this.useSub);
then this.useSub will be executed in the window scope (so inside the function, this refers to the window object) and there doesn't exist a Sub attribute.
Try:
init: function () {
var obj = this;
$(function(){obj.useSub()});
}
For some reason it does not work using $(document).ready(function(){obj.useSub()}); but it works with the $() shortcut.
Here is one way
var Test = {
useSub : function () {
Test.Sub.sayHi();
},
init: function () {
$(document).ready(Test.useSub);
},
Sub: {
sayHi: function () {
alert('hi');
}
}
};
in this line:
$(document).ready(this.useSub);
you're passing a reference to a function and the scope is lost- when the function runs, this no longer means Test.