Putting javascript inside HTML - javascript

I have two questions on placing JS inside HTML. I can't find where these two points were answered here.
Questions are basically
Let's say I put Javascript in the head, and say the script tries to get some HTML element by ID, and register a click handler for it. Now since, the HTML has not been loaded yet (because the script is called in head), will this work?
On the other hand, if register a function in head which gets called when DOM is loaded, and inside that function I put code which registers handler for click on some button, then as I understand user has chance to click button but the handler will not be called because the whole DOM was not loaded yet (and that is where the button click handler is registered). Am I correct?
What is the solution and best way to put Javascript in HTML considering above two points?

Let's say I put Javascript in the head, and say the script tries to
get some HTML element by ID, and register a click handler for it. Now
since, the HTML has not been loaded yet (because the script is called
in head), will this work?
No, this won't work. Whenever <script> tag is seen, javascript is executed right away before going further with DOM building. (if javascript is external, it will wait to fetch it over network and execute it) It will act upon the current snapshot of DOM if the javascript is using DOM APIs like getElementById. When script tries to get the element by id, it tries to get it from DOM, (All DOM APIs acts on DOM) which is being built right now. As the element is not yet added to DOM (we are processing head) it won't be able to get the element. As it cannot get the element it will throw error if you try to access addEventListener or onclick of null.
On the other hand, if register a function in head which gets called
when DOM is loaded, and inside that function I put code which
registers handler for click on some button, then as I understand user
has chance to click button but the handler will not be called because
the whole DOM was not loaded yet (and that is where the button click
handler is registered). Am I correct?
Yes, you are correct. DOM load generalises the condition that all elements are added to the DOM and is ready for any operation on any element defined by HTML.
Also, window.load will be triggered only after DOM is loaded and all the external resources like images, videos are also loaded. Using it can delay the event attachments further.
What if I want to add event listener immediately?
If you want to immediately bind an event you can do it with inline scripts, just after the element although usually not a requirement and neither a good practice.
<p>whatever</p>
<button id="mybutton">Click me</button>
<script>
document.getElementById('mybutton').addEventListener(...
</script>
<p>rest of HTML</p>
This also opens another possibility, if you put your scripts at the end of HTML instead of head, it makes sure that all elements are added to the DOM and DOM is practically ready for any operations using DOM APIs. This was used widely when listening to DOM load event was not that easy. Although I can't find the correct reference, this was a recommendation by Doug Crockford.

Related

SAPUI5 Execute Code After Page has Fully Rendered

I am injecting a Javascript-File via a Chrome-Extension on a webpage that uses SAPUI5.
I want to get the model in the binding context of some UI5-Input elements and in order to do so, I need to get to the inputs via document.getElementsByTagName. (or is there another way?)
This only works if they are already rendered. Unfortunately the ready or load events fire too early, when not everything is rendered yet.
Is there a way for me to know when the inputs have rendered?
Edit: I do not have access to the source code of the page, everything I do has to be in the injected script.
To make sure everything is renedered before firing your events, sapui5 has the function onAfterRendering.
All logic written in that function will only be executed after the control is rendered.
When a rerender of the control is rendered, the onAfterRendering is triggered again.
In the end I did it like this:
I already had event listeners attached to click and key events. Every time the handler is called, I check if document.getElementsByTagName('input') returns the inputs I need.
If yes e. g. the rendering of the inputs is complete, I set a boolean that the page is loaded completely and execute my code.

Fire event when browser parses/renders a DOM element

Assume that we know that an element (or a very specific selector) is going to appear on a page. Is it possible to set up beforehand, via JS or jQuery, an event that goes off when the browser gets to that element and parses it? This is NOT content loaded through AJAX but is present in the primary page's source.
The reason for this need is that I'm working with a hosted system that greatly limits where and when I can inject code to fix problems with the page. I can pretty much only place my code at the start and end of what is a really long page. Right now, the page has to load completely before it can inject any desired changes (yuck!). Plus, I cannot make the pages shorter in content.
This is basically the process I would like to happen:
Page begins loading
Listener set up to watch for .specialClass elements
...
.specialClass element gets parsed/added to DOM
Listener triggers function on that element
...
.specialClass element gets parsed/added to DOM
Repeat as before
...
Page finishes rendering
So, is this possible at all? Thanks in advance.

Why can $(document).on(..) be placed anywhere on a page while $(".cl").on(..) has to be placed in the $(document).ready callback or after the <body>?

Title question says it all. For the most part, I understand the latter case. When the Javascript is read, the references to the DOM have to mean something, which is why we wrap it in the $(document).ready callback, or shaft our scripts under the <body>. I really don't understand why $(document).on(...) seems to work regardless of position.
Any input would be dope.
When you do $(anything), the anything objects MUST exist at the time you run that selector.
When you're in the <head> section, the document object already exists as it's kind of the master parent for the whole page. But, nothing in the body exists yet so no $(“.class”) would exist yet and thus the selector would not find anything (or worse yet fail because the body doesn't even exist yet).
But $(document) does exist at the earliest time that you could possibly run scripts (in the <head> section so $(document) works and has something to attach the event handlers to.
If you are looking for elements in the <body> such as your example $(".class"), then you either have to wait for the <body> section to load with something like jQuery's .ready() (so those elements will exist before the script runs) or you have to physically place your script AFTER that element in the <body> so that the element you want has already been parsed by the time you run your script.
To add to this a little more, if you're using delegated event handling with .on() like this:
$(mySelector).on("click", ".myClass", fn);
Then the objects in $(mySelector) are what the event handlers will be directly attached to and those are what must exist at the time you run this line of code. The objects that match ".myClass" do not have to exist at the moment you run this code. So, if you do delegated event handling by attaching to the document object with:
$(document).on("click", ".myClass", fn);
Then, only the document object has to exist at the time you run this line of code. Since the document object is created first, it will already exist when you can run javascript code, thus it always seems to work.
This raises an interesting question about whether you can just attach all your event handlers to the document object. There are some tradeoffs if you do that (and some events only work properly if attached directly to the source object) so one shouldn't just blindly do that without understanding the consequences. See this answer for a more detailed discussion about tradeoffs in attaching all events to the document object.

Make document.ready not wait for specific DOM elements to finish loading

I found myself in a following situation. I need to somehow not include <div class="article-meta-social"></div> element and all its contents into my document.ready function. The reason is, it has links to apis from facebook, twitter, g+ etc... and Multiplied by several posts it results in a little delay before contents within document.ready function are fired off.
Therefore, what can I change in order for this
$(document).ready(function(){
});
To not wait until .article-meta-social and its contents are ready?
$(document).ready relies on the native DOMContentLoaded event, which does exactly what it says - fires when the entire DOM has been parsed. So to achieve what you want, insert the contents of .article-meta-social dynamically inside your ready handler. This way it won't hold up the main rendering of your page.

Why should $.click() be enclosed within $(document).ready()?

I am going through http://docs.jquery.com/How_jQuery_Works and find the requirement of enclosing $.click() event in $(document).ready() even a little strange. My difficulties are as follows:
When the document loads, the control will enter $(document).ready() function but will it proceed to execute $.click()? (Based on the behavior it wouldn't. But when the control enters a normal function, why wouldn't it enter $.click() function?)
Since the user can see the url only after the document is ready, is it really required to embed $.click() within $(document).ready()?
The How jQuery Works document uses the example of binding a .click() inside of $(document).ready() to be certain that the element to which the .click() event is bound has been created when the function executes.
The .click() called with a function as its parameter does not execute the .click() on the nodes matching its preceding selector, but rather binds the function to the matching nodes' onclick.
If you were to attempt to do something like this:
$('a').click(function(){
alert('clicked me');
});
...in the document <head> or before any <a> element had been rendered, the event would not get bound to any node since no node matching the $('a') selector existed at the time the function executed.
Furthermore, if you did it when some <a> tags had been created, only those already created would get the bound event. <a> tags created after the function was bound wouldn't have the .click() binding.
So in jQuery (and other JavaScript frameworks), you'll often see the convention of adding event handlers, binding widgets, etc, inside a $(document).ready(function(){});
It's not the actual call to .click() which is the concern here, but rather the selector for the element on which it's called.
For example, if there is an element with id="myButton" then you would reference it with:
$('#myButton')
However, if that element isn't yet loaded (that is, if the document isn't yet ready and you don't know what elements are currently loaded), then that selector won't find anything. So it won't call .click() on anything (either to bind the event or to fire it, depending on the argument(s) to .click()).
You can use other approaches, such as the jQuery .on() function, which can bind events to common parent elements and filter "bubbled up" events from ones added later later in the life of the document. But if you're just using a normal selector and calling a function then the selector needs to find something within the DOM in order for the function to do anything.
Yes, you need to make sure that the DOM element to which you are adding the click handler exists, any you know this by the document being ready.
The problem the $(document).ready(..) method is trying to solve is the tension between the execution of the javascript code for the page and the time at which controls in the page are bound. The ready function will fire once the document is ready and DOM elements are available hence the javascript code can make reasonable assumptions about the DOM
The most common example is the location of javascript code with respect to the DOM elements it's trying to operate on. Consider
<script>
$('#target').click(function() {
alert('It was clicked');
});
</script>
<div id="target">Click Me</div>
In this particular example the javascript will not function as intended. When the script block is entered the click line is run and there currently is no DOM element with the id target hence the handler binds to nothing. There is nothing really wrong with the code, it just had the unfortunate timing to run before the DOM element was created.
In this example the problem is obvious because the code and DOM element are visually paired together. In more complex web pages though the javascript is often in a completely separate file and has no visual guarantees about load ordering (nor should it). Using the $(document).ready(..) abstraction removes the question of ordering as a potential source of problems and facilitates proper separation of concerns
Javascript usually starts executing as soon as the tag is read. This means that in the standard practice of putting scripts in the head, there are NO elements created yet for you to bind a click handler to. Even the body element doesn't exist yet. Using jQuery.ready delays executing your code until all DOM elements have been loaded.

Categories