0

Consider the following html..

<html>
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
        <script src="test.js"></script>
    </head>
    <body>
        <button id="mybutton" type="button">submit</button>
    </body>
</html>

... when I try to implement a jQuery event handler in test.js for the click event of the button via...

$("#mybutton").on("click", function (event) {
    alert("clicked");
});

... it does not work. But if I wrap the function with a document ready as in ...

$(document).ready(function () {
    $("#mybutton").on("click", function (event) {
        alert("clicked");
    });
});

... it works. Why is that? Should all event handlers be placed in the document ready? Looking for a consistent approach.

I wish to utilize jQuery where possible. I have a difficult time understanding scope of functions with jQuery. I have seen some cases where people advocate placing the script definition in the html <head>, where others have said to place at the bottom of the body for better scoping, but I clearly struggle to understand why.

Mr Lister
  • 42,557
  • 14
  • 95
  • 136
barrypicker
  • 7,384
  • 5
  • 56
  • 68
  • Duplicate of the [question](https://stackoverflow.com/questions/799981/document-ready-equivalent-without-jquery) – Sergii Rudenko Jan 05 '18 at 01:18
  • Did you read the documentation for document ready? – epascarello Jan 05 '18 at 01:46
  • @epascarello - yes, i have read https://learn.jquery.com/using-jquery-core/document-ready/. It does not describe why event handlers are not working if defined in a script defined in the head, hence my confusion. I think there are two distinct topics - one is $(document).onReady(), the other is when jQuery creates event handlers. – barrypicker Jan 05 '18 at 02:17
  • @SergeyRudenko - the question you linked as a dup is not the same question. The question you linked is asking if there is another way to detect $(document).onReady() without using jQuery. My question is completely different. Perhaps you linked to the wrong question? – barrypicker Jan 05 '18 at 02:29

2 Answers2

3

It's because your JavaScript may be running before the content is present on the page. This is common if your script is in the head, for example. By putting it in a ready handler, you're ensuring that selecting the elements is actually possible. If this runs before the elements are present, you're attaching a click handler to an empty list of buttons.

$("#mybutton").on("click", function (event) {
  alert("clicked");
});
Jacob
  • 72,750
  • 22
  • 137
  • 214
  • I don't understand my "JavaScript may be running before the content is present on the page". This is a button click event and can only run when the button is clicked. Therefore the page must be loaded to click a button, right? – barrypicker Jan 05 '18 at 01:40
  • 2
    `$("#mybutton").on(...)` means "find all elements with the ID `mybutton` and attach a click handler". If the HTML isn't present on the page yet, then "all elements with the ID `mybutton`" will mean no elements. – Jacob Jan 05 '18 at 01:42
  • You may want to look into event delegation as another alternative. This will let you attach an event handler to something higher up, like the body, and it will capture click events for a specific set of descendents matching the selector. That enables any elements added at future points in time (like if there's some JavaScript inserting stuff) to be caught. Example: `$('body').on('click', '#mybutton', e => { ... });`. Still makes sense to use the ready handler, though, unless your script is placed below the body tag. – Jacob Jan 05 '18 at 01:46
  • Thanks for the explanation. That makes a lot more sense now. I assumed they *are* the event handler, but in reality the JavaScript I created is an instruction to create an event handler.. I had no idea... – barrypicker Jan 05 '18 at 02:01
  • @barrypicker The closure within the event listener won't run until the button is clicked, however, the code that adds the event listener will run the moment the script is run, and the script is run the moment it is loaded (unless programmed otherwise.) So if the button isn't there when the script is run, then the script won't be able to attach the event listener. – Jonathan Kuhl Jan 05 '18 at 02:01
2

Because browsers read JavaScript files top down, and the script gets read before the HTML file is loaded.

Your script is in the head. Best practice is to put your script at the bottom of the body. The reason for this is, because with the script in the head, the script will load before the HTML documents load. This is bad for two reasons:

  1. The button doesn't exist when the script runs to add the event listener, so when the button does load, it doesn't do anything

  2. if you have a massive JavaScript file, you'll create a bad user experience. Users will be forced to wait for the script to load before the HTML content loads.

If you put the JavaScript file in the bottom of the page, the page loads and the user can at least start interacting with the page and all the DOM elements will be loaded, and your JavaScript will work.

Alternatively, you can use $(document).ready() which fires an event when the document has loaded. This keeps the javascript from firing until the document has loaded. Therefore, when your script runs, there's a button, the event listener gets successfully attached to the button, and the script works.

So in short, $(document).ready() makes sure the html document is loaded before the script runs, and putting your <script> tag on the bottom of the page, also makes sure the html document is loaded before the script runs.

Jonathan Kuhl
  • 649
  • 10
  • 22
  • Thanks - when using jQuery, do you recommend always placing event handlers in the $(document).ready()? Is there a cost (i.e., overhead) of doing so? – barrypicker Jan 05 '18 at 01:38