How to access variable from Closure Scope in JavaScript - javascript

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));

Related

This Not Working Inside Function

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.

Delay eventListener

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)
});

Invoking jQuery function every x second (object literal pattern)

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.

Variable Scope: this.remove is not a function

this.remove() is not a function. How come?
var vehicle = function () {
return {
init: function () {
jQuery('.vehicle-year-profile .options .delete').bind('click', function (e) {
e.preventDefault();
this.remove();
});
},
remove: function () {
alert('test');
}
}
}();
jQuery().ready(vehicle.init);
Sorry for the confusion. I'm trying to call my own "remove" function. This is simply a class to manage vehicles on my page. This is the beginning of it and it will have a lot more functions than just init/remove.
this is a DOM element. To use jQuery's .remove() method, you need to wrap it in a jQuery object.
$(this).remove();
EDIT: If you were hoping to call the remove() function in the vehicle object, then call:
vehicle.remove();
Also, if you were hoping to shorten your .ready() call, you can do this:
jQuery(vehicle.init);
From the jQuery 1.4 release notes:
The jQuery().ready() technique still works in 1.4 but it has been deprecated. Please use either jQuery(document).ready() or jQuery(function(){}).
Maybe you're looking for something like this?
var vehicle = new function () {
var self = this;
this.init = function () {
jQuery('.vehicle-year-profile .options .delete').bind('click', function (e) {
e.preventDefault();
self.remove();
});
};
this.remove = function () {
alert('test');
};
};
...or like this maybe? It's kind of hard to tell what you're going for...
var vehicle = new function () {
function remove () {
alert('test');
}
this.init = function () {
jQuery('.vehicle-year-profile .options .delete').bind('click', function (e) {
e.preventDefault();
remove.call(this);
});
};
};
Note - we're all somewhat confused because it's not clear which "remove" function you want to call.
The problem is that you're passing in the reference to the "init" function, but when it's called the "this" variable will refer to the window object, not the value of "vehicle". Why? Because in Javascript the "this" value depends only on how a function is called. The fact that two functions are defined in the same object has absolutely nothing to do with it.
Try doing this instead:
jQuery(function() {
vehicle.init();
});
When you call the "init" function that way — by explicitly referencing it as a property of the "vehicle" object — then Javascript will bind "this" to the value of "vehicle".
edit oh wait I just noticed that you're also going to have to revise your "init" function, because that code inside the "click" handler is going to be called by jQuery in such a way as to bind "this" in that context to the affected element. Thus if you want to keep the "vehicle" reference around, you'd do this:
init: function () {
var originalThis = this;
jQuery('.vehicle-year-profile .options .delete').bind('click', function (e) {
e.preventDefault();
originalThis.remove();
});
},
Since you said you're trying to call your own remove function, here's how to do it:
var vehicle = (function () {
return {
init: function () {
var that = this; // step one
jQuery('.vehicle-year-profile .options .delete').bind('click', function (e) {
e.preventDefault();
that.remove();
});
},
remove: function () {
alert('test');
}
}
}()); // step zero - wrap the immediate invocation in parens
jQuery(function () {
vehicle.init(); // step two
);
var vehicle = function () {
return {
init: function () {
var self = this;
jQuery('.vehicle-year-profile .options .delete').bind('click', function (e) {
e.preventDefault();
self.remove();
});
},
remove: function () {
alert('test');
}
}
}();
jQuery().ready(function() {
vehicle.init();
});
When invoking a function as a method "this" refers to the object that is invoking it. In jQuery the function passed is invoked as a method of the html element so "this" becomes the element.
To make sure you are refering to the correct object you'll need to create a reference to the original object.
var vehicle = function () {
var that = {
init: function () {
jQuery('.vehicle-year-profile .options .delete').bind('click', function (e) {
e.preventDefault();
that.remove();
});
},
remove: function () {
alert('test');
}
}
return that;
}();
jQuery().ready(vehicle.init);

How do you add objects to a javascript namespace?

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.

Categories