It totaly makes sense to me to use it here.
What would be the alternative? How can i generaly avoid to use them and most of all why is it bad according to jsLint to make use of globals.
(function($){
$(function(){
$body = $('body'); //this is the BAD Global
$.each(somearray ,function(){ $body.dosomething() });
if (something){
$body.somethingelse();
}
});
}(jQuery));
Can you help me understand this? And give me a better solution?
Globals are bad because they don't cause problems right away. Only later, after you have used them all over the place, they will cause very ugly problems - which you can't solve anymore without writing your code from scratch.
Example: You use $body to define some functions. That works fine. But eventually, you also need a value. So you use $body.foo. Works fine. Then you add $body.bar. And then, weeks later, you need another value so you add $body.bar.
You test the code and it seems to work. But in fact, you have "added" the same variable twice. This is no problem because JavaScript doesn't understand the concept of "create a new variable once." It just knows "create unless it already exists." So you use your code and eventually, one function will modify $body.bar breaking another function. Even to find the problem will take you a lot of time.
That's why it is better to make sure that variables can only been seen on an as needed basis. This way, one function can't break another. This becomes more important as your code grows.
you should define it with var $body, then it would be local in the scope of that function, without var it could be overwritten by everybody
(function($){
$(function(){
var $body = $('body'); //this is the local variable
$.each(somearray ,function(){ $body.dosomething() });
if (something){
$body.somethingelse();
}
});
}(jQuery));
Globale variables could clash with other Scripts or be overwritten. When you don't need a global, it's advisable to avoid them. Simply use var (or let if your JS-Version-Support is greater than 1.7):
(function() {
var foo = 'bar';
alert(foo);
})();
You could rewrite that as
var $body = $('body');
That (the use of the var keyword) would make it a local variable, which is enough for your purposes. It will still be within scope in your each callback.
The reason it's bad to use globals is that it can be overwritten by anything else. For your code to scale well, it becomes dependent on what other scripts you use. It's preferable to keep the script as self-sufficient as possible, with as little as possible dependencies pointing to the world outside of it.
jsLint is very stringent. It's probably not necessary to get too hung up about it.
But if you feel bad, you can do it just like how you scoped jQuery:
(function($){
$(function(){
$.each(somearray ,(function($body){ $body.dosomething() })($('body'));
if (something){
$('body').somethingelse();
}
});
}(jQuery));
Related
I've been able to find a few similar questions but I feel that the answers provided within do not fully expel my confusion.
I have come across this question whilst playing with jQuery, but I guess that this is more of a JS question than jQuery specific.
I feel like in the below example these variables that I wish to define would be good candidates to be global, they have a wide-ranging use outside of a few functions, but I feel that I want to limit the exposure.
$(function() {
$containers = $('.container');
$childContainer = $('#childContainer');
//Removed extra code
var $aButton = $('#childButton');
//Removed extra code
$containers.hide();
$childContainer.show(400);
$aButton.on('click', clickChildButton);
//Removed extra code
};
function clickChildButton() {
$containers.hide(400);
$childContainer.show(400);
}
I will have a number of buttons showing/hiding various containers. In all cases the $containers variable will need to be visible to the other functions to allow it to be hidden.
Should I be using global variables (or perhaps a namespacing global object hack) or is there another way that I can limit the scope of the $containers variable?
I'm not too keen on using anonymous functions to handle the click events as they are going to start getting a bit more complex (and contain more than just the two lines shown in the clickChildButton function.
Note: In this particular example it might be better to refactor the code and create a hideContainers function, but I am more interested in how to control the scope of variables in general rather than this particular example.
Thanks
In JavaScript (prior to ES6), all variables are function-scoped. Consequently, the only way to scope a variable is to make it local to a function.
You have two basic choices here. One is to make clickChildButton local to $(function(...) {...}), as it is the only place where it is relevant:
$(function() {
var $containers, $childContainer;
function clickChildButton() {
$containers.hide(400);
$childContainer.show(400);
}
...
});
If you need the scope to actually be wider but not too wide, the other choice is to wrap everything into an IIFE:
(function() {
$(function() {
...
});
function clickChildButton() {
....
});
)();
for some reason I do that every time because I find it clean. I declare variables on top to use them below. I do that even if I use them only once.
Here is an example (using jQuery framework) :
$("#tbListing").delegate("a.btnEdit", "click", function(e) {
var storeId = $(this).closest("tr").attr("id").replace("store-", ""),
storeName = $(this).closest("tr").find("td:eq(1)").html(),
$currentRow = $(this).closest("tr");
$currentRow.addClass("highlight");
$("#dialogStore")
.data("mode", "edit")
.data("storeId", storeId)
.data("storeName", storeName)
.dialog( "open" );
e.preventDefault();
});
I tend to do that in PHP too. Am I right if I believe it's not very memory efficient to do that ?
Edit: Thank you for all the answers. You have all given good answers. About that code optimisation now. Is that better now ?
$("#tbListing").delegate("a.btnEdit", "click", function(e) {
var $currentRow = $(this).closest("tr"),
storeId = this.rel, /*storing the storeId in the edit button's rel attribute now*/
storeName = $currentRow.find("td:eq(1)").html();
$currentRow.addClass("highlight");
$("#dialogStore")
.data("info", {
"mode" : "edit",
"storeId" : storeId,
"storeName" : storeName
}) /*can anyone confirm that overusing the data isn't very efficient*/
.dialog( "open" );
e.preventDefault();
});
Sorry, are you asking if it's OK to declare variables even if you're using them once?
Absolutely! It makes the code a million times more readable if you name things properly with a variable. Readability should be your primary concern. Memory efficiency should only be a concern if it proves problematic.
As Knuth said,
We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.
If you're asking more about declaring the variables at the beginning of the function, rather than where they are first used, then Emmett has it right - Crockford recommends doing this in JavaScript to avoid scope-related confusion. Whether it's worth it in PHP is a purely subjective question I'd say, but there's nothing wrong with keeping your PHP and JS coding styles similar.
One more CS quote (from Abelson and Sussman's SICP):
programs must be written for people to read, and only incidentally for machines to execute.
It's not bad practice.
The var statements should be the first statements in the function body.
JavaScript does not have block scope,
so defining variables in blocks can
confuse programmers who are
experienced with other C family
languages. Define all variables at the
top of the function.
http://javascript.crockford.com/code.html
Declaring variables at the top is a good thing to do. It makes the code more readable. In your particular example, you could replace $(this).closest('tr') witha variable, as suggested int eh comments, but in general I find code with descriptive variable names all in one place very readable.
nah, I'd say you're doing exactly the right thing.
As #Caspar says, you could simplify your code by setting $currentRow first and using that instead of $(this).closest("tr") in the other two lines. And there may be a few other things you could improve. But setting vars at the begining of a function the way you've done it is absolutely a good thing.
Particuarly good because you've done it inside the function, so they're local variables, which means they get thrown away at the end of the function, so no memory usage issues there.
If you'd set them as global vars, it might have been more of an issue, although to be honest even then, since you're just setting pointers to an existing object, it wouldn't be using a huge amount of memory even then (though it would be polluting the global namespace, which isn't good)
Is there any reason why I shouldn't do something like the following (to avoid using a hidden field to store the temporary information)? I'm using jQuery syntax for brevity but it's a general JavaScript question.
function editComments() {
window.currentComments = $('#commentsTextBox').val();
$('#commentsTextBox').removeAttr("readonly");
}
function cancelEditComments() {
$('#commentsTextBox').val(window.currentComments);
$('#commentsTextBox').attr("readonly", "readonly");
}
I know that globals are generally considered bad practice, but is there really any problem with doing the above?
Please don't answer/comment with "globals variables are evil" unless you can give a reason/explanation.
There's no real problem with this except that global variables are evil. ;)
However, if you are using jQuery anyway, in my opinion, a much nicer way is to store it in the element using data():
function editComments() {
$('#commentsTextBox').data("oldValue", $('#commentsTextBox').val());
$('#commentsTextBox').removeAttr("readonly", "readonly");
}
function cancelEditComments() {
var oldValue = $('#commentsTextBox').data("oldValue");
$('#commentsTextBox').val(oldValue );
$('#commentsTextBox').attr("readonly", "readonly");
}
As long as you keep it inside the script, and nothing else gets done with the element, that should work fine.
The problem with globals in javascript (on top of that of any other languages). Is that there is no mechanism to resolve name clashes (or rather, the mechanism is to just assume that it's the same variable). If you use a global variable called currentComments and also include some other module with a currentComments global variable then one of them is going to lose, and you may get unpredictable results.
It would be better to use one that is scoped to your module, thus:
(function(){
var currentComments;
function editComments() {
currentComments = $('#commentsTextBox').val();
$('#commentsTextBox').removeAttr("readonly", "readonly");
}
function cancelEditComments() {
$('#commentsTextBox').val(currentComments);
$('#commentsTextBox').attr("readonly", "readonly");
}
}());
There's no real reason not to do it, if you ignore the "global variables are bad" argument.
One thing you need to be aware of is you can't .delete properties from the window object in IE, it causes an exception to be thrown. In your case, since it's just a string, it probably doesn't matter.
This fails on IE:
window.foo = 'bar';
delete window.foo;
I'm trying to find a way to minimize the number of Selector look-ups. My issue is that I have a variable defined with base $(document).ready() that needs to be updated inside functions nested inside $(document).ready()
Consider this example (EDIT: I updated it to be more explanatory)
<script>
//var $current_page = $home_page; **<--I DONT want to do this, going global
and of course it doesn't work since
$home_page isn't defined yet.**
$(document).ready(function() {
var $home_page = $("#home-page");
var $portfolio_page = $("#portfolio-page");
var $about_page = $("#about-page");
var $current_page = $home_page; // **<--This variable, in this scope level,
// is what I want updated**
$("#home-btn").click(function () {
$current_page.stop()
$current_page.show()
$current_page.animate({
duration: 360,
easing: 'easeInCirc',
complete: function() {
$(this).css({ top: -700 });
}
);
$current_page = $home_page;
});
$("#portfolio-btn").click(function () {
$current_page.stop()
$current_page.show()
$current_page.animate({
duration: 360,
easing: 'easeInCirc',
complete: function() {
$(this).css({ top: -700 });
}
);
$current_page = $portfolio_page; //<--This needs to somehow update the
// variable in the $(document).ready
// scope, but not global**
});
});
<script>
How can I update the variable $current_page without making it a global variable?
EDIT:
This is done to animate out the current page div when you click on a menu item. Yes, it's missing things, yes it may not make sense. It's just an example, not the actual application.
I understand this example is still trivial for performance, just ignore that fact. I just want to know how to do achieve this, not a lesson on whether it's the best practice or performance. Thanks guys.
The inner function creates a closure, capturing the variables in the scope it is defined in. So you already have what you're asking for...
...whether that's a good idea or not is another matter.
For starters, you're not actually modifying the value in the code you listed - you're assigning $current_page the same value it was already initialized with.
But assuming you just omitted the code that you would normally use to pick a different value for $current_page, you need to ask yourself: is this really even necessary? You're performing a lookup based on an element ID and caching a reference to that element in a variable without knowing if or when you'll actually need it again. At best, this results in a potentially-unnecessary lookup; at worst, it can result in a memory leak. Why not just keep track of the ID itself, and look up the element when and where you actually need it? Don't worry about performance until you actually encounter a performance problem... or you may find that your premature optimization has caused more problems than it solves.
Same goes for $home_page, $portfolio_page and $about_page - you're making your page load (slightly) more slowly on the off-chance that you'll need a reference to those elements later on, when you could just as well look them up as-needed.
"How can I update the variable $current_page without making it a global variable?"
You can update it right now. The inner click-handler function can modify $current_page.
"I'm trying to find a way to minimize the number of Selector look-ups."
But it seems that in fact you're about to make more, if you're changing $current_page with another selector.
But it isn't really clear what you're really trying to do.
I'm sure there are a million posts about this out there, but surprisingly I'm having trouble finding something.
I have a simple script where I want to set the onClick handler for an <A> link on initialization of the page.
When I run this I immediately get a 'foo' alert box where I expected to only get an alert when I click on the link.
What stupid thing am I doing wrong? (I've tried click= and onClick=)...
<script language="javascript">
function init(){
document.getElementById("foo").click = new function() { alert('foo'); };
}
</script>
<body onload="init()">
<a id="foo" href=#>Click to run foo</a>
</body>
Edit: I changed my accepted answer to a jQuery answer. The answer by 'Már Örlygsson' is technically the correct answer to my original question (click should be onclick and new should be removed) but I strongly discourage anyone from using 'document.getElementById(...) directly in their code - and to use jQuery instead.
Try:
document.getElementById("foo").onclick = function (){alert('foo');};
Use .onclick (all lowercase). Like so:
document.getElementById("foo").onclick = function () {
alert('foo'); // do your stuff
return false; // <-- to suppress the default link behaviour
};
Actually, you'll probably find yourself way better off using some good library (I recommend jQuery for several reasons) to get you up and running, and writing clean javascript.
Cross-browser (in)compatibilities are a right hell to deal with for anyone - let alone someone who's just starting.
jQuery:
$('#foo').click(function() { alert('foo'); });
Or if you don't want it to follow the link href:
$('#foo').click(function() { alert('foo'); return false; });
I tried more or less all of the other solutions the other day, but none of them worked for me until I tried this one:
var submitButton = document.getElementById('submitButton');
submitButton.setAttribute('onclick', 'alert("hello");');
As far as I can tell, it works perfectly.
If you want to pass variables from the current function, another way to do this is, for example:
document.getElementById("space1").onclick = new Function("lrgWithInfo('"+myVar+"')");
If you don't need to pass information from this function, it's just:
document.getElementById("space1").onclick = new Function("lrgWithInfo('13')");
OMG... It's not only a problem of "jQuery Library" and "getElementById".
Sure, jQuery helps us to put cross-browser problems aside, but using the traditional way without libraries can still work well, if you really understand JavaScript ENOUGH!!!
Both #Már Örlygsson and #Darryl Hein gave you good ALTARNATIVES(I'd say, they're just altarnatives, not anwsers), where the former used the traditional way, and the latter jQuery way. But do you really know the answer to your problem? What is wrong with your code?
First, .click is a jQuery way. If you want to use traditional way, use .onclick instead. Or I recommend you concentrating on learning to use jQuery only, in case of confusing. jQuery is a good tool to use without knowing DOM enough.
The second problem, also the critical one, new function(){} is a very bad syntax, or say it is a wrong syntax.
No matter whether you want to go with jQuery or without it, you need to clarify it.
There are 3 basic ways declaring function:
function name () {code}
... = function() {code} // known as anonymous function or function literal
... = new Function("code") // Function Object
Note that javascript is case-sensitive, so new function() is not a standard syntax of javascript. Browsers may misunderstand the meaning.
Thus your code can be modified using the second way as
= function(){alert();}
Or using the third way as
= new Function("alert();");
Elaborating on it, the second way works almost the same as the third way, and the second way is very common, while the third is rare. Both of your best answers use the second way.
However, the third way can do something that the second can't do, because of "runtime" and "compile time". I just hope you know new Function() can be useful sometimes. One day you meet problems using function(){}, don't forget new Function().
To understand more, you are recommended read << JavaScript: The Definitive Guide, 6th Edition >>, O'Reilly.
I agree that using jQuery is the best option. You should also avoid using body's onload function and use jQuery's ready function instead. As for the event listeners, they should be functions that take one argument:
document.getElementById("foo").onclick = function (event){alert('foo');};
or in jQuery:
$('#foo').click(function(event) { alert('foo'); }
Here is the YUI counterpart to the jQuery posts above.
<script>
YAHOO.util.Event.onDOMReady(function() {
document.getElementById("foo").onclick = function (){alert('foo');};
});
</script>
I think you want to use jQuery's .bind and .unBind methods. In my testing, changing the click event using .click and .onclick actually called the newly assigned event, resulting in a never-ending loop.
For example, if the events you are toggling between are hide() and unHide(), and clicking one switches the click event to the other, you would end up in a continuous loop. A better way would be to do this:
$(element).unbind().bind( 'click' , function(){ alert('!') } );
Nobody addressed the actual problem which was happening, to explain why the alert was issued.
This code: document.getElementById("foo").click = new function() { alert('foo'); }; assigns the click property of the #foo element to an empty object. The anonymous function in here is meant to initialize the object. I like to think of this type of function as a constructor. You put the alert in there, so it gets called because the function gets called immediately.
See this question.
The YUI example above should really be:
<script>
YAHOO.util.Event.onDOMReady(function() {
Dom.get("foo").onclick = function (){alert('foo');};
});
</script>