Should I use addEventListener in these type cases?
<input id="input" type="file" onchange="fun()>
or
document.getElementById("input").addEventListener("change", function() {
fun();
});
and why?
The onchange attribute requires the fun function to be in global scope. In a larger application you want to avoid this, as there might be other functions with the same name from your application or from external libraries. Or imagine building a component that is used several times on the page.
addEventListener can be wrapped in a closure like this and be used inside an isolated component:
(function(){})(
function fun(){
// Now other components can also have a `fun` function, without causing confusion.
// `fun` is only defined inside the IIFE (the (function(){})() wrapper around the module).
}
document.getElementById("input").addEventListener("change", function() {
fun();
});
);
Related
I've been seeing a number of people wrapping their controllers with:
function(){
//CODE
}();
What's the benefit / goal there?
That isn't really anything related directly to Angular, it is a JS pattern know as an Immediately Invoked Function Expression.
It is one of the most useful patterns in JavaScript, primarily because of:
Code Encapsulation
Since functions have closures in JS, we can use this pattern to create private data very easily:
var index = (function iife() {
var counter = 0; // <-- this is private, only available inside the closure of inner()
return function inner() {
return counter++;
};
})();
console.log(index()); // 0
console.log(index()); // 1
console.log(index()); // 2
console.log(index.counter) // undefined
We can also pass arguments into an IIFE, which allows us to control how we access the outside context of our IIFE. For example, to make sure $ is actually the jQuery object within your code:
(function ($) {
// here have access to the global jQuery as $ regardless of what window.$ is and
// how many libraries are trying to use $
})(jQuery);
By combining the two ideas above, IIFEs can also be used to implement the module pattern, which is the basis for how RequireJS and NodeJS separate code:
var myModule = (function iife(initData) {
// we can use initData here to initialize our module if necessary
var module = {};
// private vars ...
var privateVar1, privateVar2;
// private methods ...
function privateMethod() {
console.log('yeeey');
}
module.somePublicProperty = 1;
module.somePublicMethod = function() {
privateMethod();
};
return module;
})(/* pass initialization data */);
myModule.somePublicMethod(); // 'yeeey'
The reason you'll find a lot of JavaScript code wrapped in an anonymous function is to isolate it from other code on the page.
The following code will declare a variable called name on a global scope:
var name = "Hello World";
By using that code, any other script on the page attempting to use a variable called name could potentially get an unexpected value of "Hello World" because your script declared it as "Hello World".
By wrapping that code in an anonymous function, you keep the code from conflicting with other variables called name:
(function() {
var name = "Hello World";
})();
In the example above, name is now only available inside of the scope of the anonymous function. It is not global, and therefore cannot conflict with other code on the page.
By wrapping your Angular module in an anonymous function, you prevent your code from conflicting with other code.
Additionally, other people who may use your code won't have to worry about it changing their global scope.
IMHO it's not necessary, even superfluous, as most controllers are already functions:
'use strict';
angular.module('MyApp').controller('AboutController', ['$scope'
function ($scope) {
$scope.title = 'About Us';
}
]);
Everything #nem035 and #tcasey said is correct but it also have another side effect.
If you use tools like Grunt or Gulp it also allow you to have working dists to put in production.
If you do not use Immediate Invoke Pattern you'll most likely have minification problems like:
State X is already defined!
Unknown provider
. . .
I suggest you to wrap all your js modules with this pattern.
I hope I've been helpful.
There are many benefits of you immediately-invoked-function-expression short IIFE and it's best practice to use it. This way each of angular service or controller become isolate and you don't run into global variables as it can happened if you don't use IIFE.
read more about it
I have a simple index.html file with some dragable images. My JS file looks like this:
function drop(ev){
ev.preventDefault();
var data = ev.dataTransfer.getData('text');
ev.target.appendChild(document.getElementById(data));
}
function drag(ev){
ev.dataTransfer.setData('text',ev.target.id);
}
That works perfectly fine. But, as I learned, it's good practice to write your code within a self-executed block.
So, I moved my two functions:
(function(){
function drop(ev){
ev.preventDefault();
var data = ev.dataTransfer.getData('text');
ev.target.appendChild(document.getElementById(data));
}
function drag(ev){
ev.dataTransfer.setData('text',ev.target.id);
}
})();
And now, when dragging the images around, the functions appear to be 'undefined'. They're probably newbie questions but:
Can I keep my functions in the block scope and still use them on the HTML like <div id="div0" ondrop="drop(event)">...</div>?
If not, why?
The whole point of enclosing stuff into IIFEs is to isolate them and not pollute the global environment with unnecessaries. For example,
var counter = (function() {
var current = 0;
function getCurrentAndIncrement() {
return current++;
};
return getCurrentAndIncrement;
})();
counter()
// 0
counter()
// 1
current
// ReferenceError: current is not defined
We hide what should be private, and expose what should be public.
You hid everything - then the fact that it is inaccessible should not be a surprise.
Finally, <div id="div0" ondrop="drop(event)">...</div> is a bad practice. Binding event listeners from JavaScript is much better, if a bit more complicated. Do just this:
<div id="div0">...</div>
then in JavaScript,
var div0 = document.getElementById('div0');
div0.addEventListener('drop', myHandler);
and this can, indeed, be stuffed into an IIFE. Whatever you refer to from HTML (as in your drop function) needs to be in global scope, or the browser will not be able to find it.
The answer is scope. The variables and functions defined within a function are only accessible within that function. In your first example, you're defining them globally; in your second example, they're inside a function.
Can I keep my functions in the block scope and still use them on the HTML like <div id="div0" ondrop="drop(event)">...</div>?
To use functions from old-fashioned onxyz attribute handlers, they have to be globals. You can make your functions globals by assigning them to properties on the global object, which you can access as window on browsers:
(function() {
function drop(ev) {
ev.preventDefault();
var data = ev.dataTransfer.getData('text');
ev.target.appendChild(document.getElementById(data));
}
function drag(ev) {
ev.dataTransfer.setData('text', ev.target.id);
}
window.drop = drop;
window.drag = drag;
})();
...but ideally, it's better not to create globals; the global namespace is incredibly crowded, adding to it just asks for conflicts. Instead, use modern event handling (addEventListener, attachEvent) instead of onxyz attributes.
The inner function wont be executed. Here comes the beauty of closure make js objects private & public . This code may serve your purpose
var demoFunction =(function(){
function _drop(ev){
alert("Helo");
_drag();
}
function _drag(ev){
alert("Hello1");
}
_drop();
return{
drop :_drop,
drag:_drag
}
})();
You can also call this drop and drag from other functions by demoFunction.drop & so on.Hope this is helpful for you
jsfiddle
I dinamically add divs with onlick event, but clicking got an error (Mozilla Firefox): "ReferenceError: myfoo is not defined". If I change onclick event to alert, it works fine, but non with mysefl written functions.
Here is jsfiddle
http://jsfiddle.net/UJ85S/5/
function myfoo(x)
{
alert(x);
}
$("#some").html('<div id="cool_div" onclick="myfoo('+"'xwe'"+');"></div>');
Can you, please, explain what is wrong?
(I understant that can assign.click event, but is it possible through onclick?).
What you really need to do is not let jsFiddle wrap it inside the onload event as this uses a function which creates new scope. Your function is then not accessible outside this new scope. Learn what's happening not learn how to get around it (i.e. not just hack your code to the window Object):
http://jsfiddle.net/UJ85S/12/
No wrap - in <body>
This is happening because you define myfoo inside of $(window).load(function () {...}) function (JSFIDDLE does this):
You need to declare a global function. You can do window.myfoo to declare your function instead.
window.myfoo = function (x)
{
alert(x);
}
JSFIDDLE
But yeah, it's not a good practice to polute the global scope, that's why it's better to use $(...).on("click", function () { alert(...) }) handlers.
I discourage using on... attributes in HTML because it's also another bad practice.
Your code becomes:
function myfoo (x)
{
alert(x);
}
var $divToAppend = $("<div id='cool_div'>")
$divToAppend.on("click", function () {
myfoo("hello");
});
$("#some").html($divToAppend);
And here a DEMO.
In and external JS file I have
$(document).ready(function() {
var example = function(){ alert("hello") }
});
and I want to call that function from my html, how would I do that?
<img src="..." ondblclick="example()" />
n.b. I'm aware of jquery dblclick() but curious about how to correctly do the above.
$(document).ready(function() {
window.example = function() {
alert("hello")
}
});
Or define it outside, if possible. It doesn't look like it has to be defined inside document ready at all.
The other solutions here will work, but structurally in your project, the best solution is to remove the event handling code from the HTML and hook up the event entirely via javascript (separate the HTML/JS). Since you already have jQuery in your project, this is very easy. To do that, all you need to do is to put some sort of identification on the image:
<img id="example" src="..." />
Then, in you can just hook up the event code in your ready() function like this
$(document).ready(function() {
$("#example").dblclick(function() {
alert("Hello");
});
});
This has the following advantages:
It creates no global variables - reducing the global namespace pollution.
It separates the HTML from the javascript which keeps all code governing the behavior in one compact spot and is usually a good thing
Using event listeners is a bit more scalable than using .ondblclick - allowing multiple different parts of code to use non-conflicting event handlers on the same object
Your function should be global (in fact, property of window object) if you want to access it from HTML. But best practice is to avoid global variables and functions, using namespace instead:
// let's publish our namespace to window object
if (!window.myNamespace){
// creating an empty global object
var myNamespace = {};
}
// and add our function to it
$(document).ready(function() {
myNamespace.example = function(){ alert("hello"); }
});
We can use it in HTML like this:
<img src="..." ondblclick="myNamespace.example()" />
The best option would be to simply define the function outside document.ready(). There is no reason defining the function within the $(document).ready() event is necessary, as if you call the function within the $(document).ready() function, the document is guarenteed to be ready.
However, you can also define the function on the global window object, like so:
$(document).ready(function() {
window.example = function(){ alert("hello") }
});
You can either move the function declaration outside of the DOM-ready handler:
function example(){ alert("hello") }
$(document).ready(function() {
// code
});
But the better solution is to keep JavaScript in your .js files and avoid the inline event handlers. Give your element an id and fetch it:
<img src="..." id="imgid" />
$(document).ready(function() {
document.getElementById("imgid").ondblclick = function(){ alert("hello") }
});
#Esailija has answered it correctly but if you want to keep it as it is then simply remove var and make it global.
var example;
$(document).ready(function() {
example = function() {
alert("hello")
}
});
If you do not put var the variable/function/object becomes global. Using var you were setting its context within document.ready function.
I'm working on a proprietary site, and I'm having some issues. I'm using jQuery along with prototype, and I've got it namespaced properly, so in this question assume you can use $ or jQ as a namespaced reference to jQuery.
So I've got a bunch of functions, some mix jQuery and javascript, some plain javascript, some jQuery only. Now, currently some functions are defined within the document.ready jQuery function, and some are defined outside of it, kind of like this:
jQ(document.ready(function($) {
if ( ifConfig ) {
//page check, function calls here
fnc1();
fnc2();
fnc3();
fnc4();
}
function fnc1() {
//fnc code in here
}
function fnc2() {
//fnc code in here
}
}); //end document.ready
function fnc3() {
}
function fnc4() {
}
Now this is all pseudo code, you can assume the functions are valid and have valid code in them. Recently I was doing some debugging, and one of my functions that was declared and called inside the document.ready said it was undefined. I moved it outside of the document.ready, and everything worked again.
I'm basically trying to understand the order of how functions are initiated/called better, so my question is when do you declare functions inside the document.ready and when do you declare them outside? Do you only declare inside when they're called within that document.ready only? Or should I always just declare them outside of that document.ready?
Thanks.
Generally, you should declare & define your own namespace, where all of your application logic (including functions/methods) is located. That way you avoid collision with other scripts on your site + that way your code is much cleaner and easier to maintenaine.
var myapp = function(){
var foobar1 = null,
foobar2 = null,
foobar3 = null;
return {
getFoobar1: function(){
return foobar1;
},
getFoobar2: function(){
return foobar2;
},
setFoobar1: function(foo){
foobar1 = foo;
},
clickhandler: function(e){
alert('I am an event handler, and I am not anonymous');
}
// etc.
};
};
$(document).ready(function(){
var Application = myapp();
Application.getFoobar2();
$(document).bind('click', Application.clickhandler);
});
That pattern (some call it the "method pattern") creates a closured function/object which also guarantees private member variables within your namespace, only accessible through the getter functions from the outside.
This is really only a pretty basic example, you can push this idea & pattern to an extend, which is very nice & a good thing (IMO).
A great book about this stuff which was named and recommended pretty often is "Javascript: The Good Parts" by Douglas Crockford.
If a function is only used inside the document ready function, then declare it inside so you don't pollute the global scope. Otherwise, declare it outside so it the rest of your script has access to those functions.
(document).ready is more used for things that need to be executed at page load, and not function declarations. If you declare them inside of (document).ready, their scope will be local to that block - if they're only used locally, that's fine and they should be declared there. Otherwise, declare them outside.
So in your example, if the functions are only used in that block, they should be declared in there. If they're used other places additionally, they should be declared outside.