I just don't get it. I searched and searched but for this I just can't figure out what's "right".
There are three examples.
1) Fiddle 1.0
Here we have html
with onlick="function"
and the javascript
function right there as well which works fine
<span class="classic one" onclick="someFunction(this,'one')">CLICK HERE</span>
<script type="text/javascript">
function someFunction(obj,nr) {
var string = $(obj).attr('class');
$('.result').text( string );
}
</script>
2) Fiddle 2.0 Then when I move the function to the script section (kind of move it to the .js file) I get an error "ReferenceError: someFunction is not defined"
THIS IS WHERE THE QUESTION BEGINS
3) Fiddle 3 So now I have a function in document ready calling with .on(click which always works fine. This function is calling another function which is outside the docuemnt.ready() and also works fine.
So the question. When do I have to define the functions where AND WHY so it always works?
Thank you!
All the code from Example 3) looks like this:
<div class="result">result</div>
<span class="classic one" onclick="someFunction(this,'one')">CLICK HERE</span>
<span class="classic two" onclick="someFunction(this,'two')">CLICK HERE</span>
<span class="classic three" onclick="someFunction(this,'three')">CLICK HERE</span>
<span class="classic four" onclick="someFunction(this,'four')">CLICK HERE</span>
<div class="ready">ready</div>
<span class="callOtherFunction">Call other function</span>
<script type="text/javascript">
$(document).ready(function(){
$('.ready').text( 'dom is ready' );
function someFunction(obj,nr) {
var string = $(obj).attr('class');
$('.result').text( string );
}
$( "span.callOtherFunction" ).on({
click: function() {
$(this).css("color","red");
$(this).addClass("xyz");
otherFunctionCallingFunction($(this));
}
});
});
function otherFunctionCallingFunction($me) {
$('.callOtherFunction').append( ' --> ' + $me.attr('class') );
}
</script>
A lot of what you're seeing is because of jsFiddle's very surprising default setting, which is to wrap the code in the script pane in an onload
handler. So your code is wrapped in a function and no longer at global scope (which is where functions need to be if you use onclick
-style attributes). You can change this with the drop-down box on the left (the second one, under the list of libraries and scripts). Change it to "no wrap" to have unwrapped code.
You're not (by far!) the first to be bit by this surprising default.
Answering your main question:
when has a function to be in $(document).ready()
If you control where the script
tags loading your script go, you basically never have to use ready
; instead, just make sure your script
tags are at the end of the HTML, just before the closing </body>
.
You can use ready
, of course. The reason for doing so is to make sure that all the DOM elements have been created before your code runs. But if you put your script
tag at the end, that's already true. You can still define your functions outside of the ready
handler (if you want them to be globals), but if you're using ready
, you would call them from the ready
handler so the elements exist.
FWIW, I would avoid using onclick
-style attributes for hooking up event handlers, primarily because they require you to create global functions. I prefer to avoid creating any global symbols when I can avoid it.
The general form I'd recommend:
<!-- ...your page markup here... -->
<script src="any_libraries_you_need_if_you_are_not_combining.js"></script>
<script src="your_script.js"></script>
</body>
</html>
where your script looks like this:
(function($) { // A scoping function that wraps everything
// Hook up event handlers here. You can use `$` for jQuery, even if
// you've used noConflict, because we have a private `$` symbol (see
// the `$` argument above)
// The body of your code goes here
})(jQuery); // <== Note how we're passing the jQuery reference in
// and immediately executing the scoping function
Here's a complete example: Live Copy
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Script Placement Example</title>
</head>
<body>
<input type="button" class="hello-button" value="Click me">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<script>
// This would be in a file, rather than inline
(function($) {
// Hook up handlers
$(".hello-button").click(sayHello);
// Body of the code
function sayHello() {
$("<p>Hello!</p>").appendTo(document.body);
}
})(jQuery);
</script>
</body>
</html>
In JavaScript, scope is handled on a per-function basis. Anything in scope of a function can access other things in scope of that function and things in a wider scope.
Defining a variable with var
inside a function will limit the scope of that variable to the function.
Defining a function with a function declaration inside another function will limit the scope of the defined function to the container function.
When you use ready
, you pass a function to it. Anything defined in that function is, of course, scoped to that function. Since it is scoped to that function, it is not a global. Your onclick attribute isn't defined in the scope of that function, it can only access globals. This is why you get a reference error.
Avoid globals. They make things hard to maintain. Avoid onclick
attributes, they usually depend on globals.
You need to use ready
if you want code to run after the DOM has been fully constructed. This is useful if you want to bind event handlers to elements in it with JS (e.g. with jQuery.on
). Do this rather then using onclick
attributes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With