Explain this DOM traversal order - javascript

I wrote the following page as a DOM traversal demo:
<html>
<head>
<title>DOM Traversal</title>
</head>
<body>
<h1>Sample H1</h1>
<div id="text">
<p>Sample paragraph</p>
</div>
</body>
<script>
// Traversing the DOM tree
"use strict";
var node = document.body;
while(node) {
console.log(node);
node = node.lastChild;
}
</script>
</html>
Surprisingly, the output I'm getting is the body tag followed by the script tag. How is this possible? Isn't the script tag a sibling of the body tag? Also, why aren't the child nodes of body being traversed?

You cannot add your script element outside of the head or body elements. The browser is auto-correcting your HTML, moving the script into the end of your body, which explains the result you are getting.
The html element may only contain the head and body elements as its children. Anything else must be placed within these two elements.

On top of what iMoses said, your script block will run before the entire document is parsed, as it doesn't wait on the domReady event. This seems to cause a race condition.
If you leave your script where it is, but wait for domReady, you get a slightly different result (albeit still not what you want).
EDIT: change your script to output "node.outerHTML" instead, and you will see that the script block gets moved or rather duplicated into the body.
Without waiting for document ready, you end up with two script blocks while the script is running - one is your original, the other one at the end of the body as iMoses pointed out.
Waiting for document ready, you will find that only the (moved) one inside the body remains.

A good place to start with resolving this kind of 'issue' is to validate the source first.
Tools such as the W3C HTML validator will help you avoid this kind of problem going forward:
http://validator.w3.org/check
Validating your code returns the following:
Line 11, Column 12: document type does not allow element "SCRIPT" here
<script>
The element named above was found in a context where it is not allowed. This could mean that you have incorrectly nested elements -- such as a "style" element in the "body" section instead of inside "head" -- or two elements that overlap (which is not allowed).
One common cause for this error is the use of XHTML syntax in HTML documents. Due to HTML's rules of implicitly closed elements, this error can create cascading effects. For instance, using XHTML's "self-closing" tags for "meta" and "link" in the "head" section of a HTML document may cause the parser to infer the end of the "head" section and the beginning of the "body" section (where "link" and "meta" are not allowed; hence the reported error).

Related

jQuery parses raw HTML with paragraph wrong

The screenshot shows the firebug watch window.
Why does it parse the almost same HTMLs wrong? I expected there would be just one element in the second row, instead of an array of elements.
The browser is not wrong. <p><div></div></p> is invalid HTML.
The reason why the browser analyses different the two codes is because <p> elements are only allowed to contain inline elements.
Both <p> and <div> are block elements but <p> cannot contain a <div> which is not phrasing content. So when the browser reads that code he finds the element <p> and then a unexpected <div>. Browsers are very tolerant to markup errors, so the browser closes the p tag and goes to the next div element. Then comes the third element, (also wrong HTML because it misses the opening tag) so it's read as a new element.
In the first case you have nested elements, so the browser shows one element;
In the second case you have three elements in the same DOM tree level, so an array of elements is the browsers answer.
They both render but the wrong one can produce unexpected results. How the browser will read wrong markup plus CSS will be difficult to predict.
So, the browser reads/parses the code as: <p></p><div></div><p></p>, giving you different results.
Worth to read:
W3 / HTML5 spec:
p – paragraph
div – generic flow container.
MOZILLA DEVELOPER NETWORK:
MDN: p element (check "Permitted content")
MDN: block-level elements
The result is not wrong in either case.
The <p> HTML tag may only contain phrasing content elements. However, <div> is not phrasing content (but a flow element). (Simplified <p> may contain inline elements, but <div> is a block element.) Thus, the HTML code from your second example is invalid (as in not standard conforming).
What happens as a result is that the browsers HTML to DOM parser - which is triggered by jQuery of course - handles the HTML as follows:
Identify <p> block being opened
Identify <div> block being opened
Notice a div block is invalid within the previously opened <p>
Close the previous <p> block
…
So an equivalent HTML code would be <p></p><div></div><p></p>, which is valid HTML. So the parser corrects the HTML for you.
Because we now have three top level elements rather than nested elements with one top level element your get an array of DOM elements rather than one element like you expected.
Webbrowsers are very robust against non-standard conformant HTML code. The behaviour you noticed and pointed out here is one of the many examples where the parser makes sense out of invalid HTML code as a best effort.
References:
W3: HTML markup: p
W3: HTML markup: div
MDN p

Targeting an ID that is rendered by a script

I'm using a nifty little script called Tabifier (http://www.barelyfitz.com/projects/tabber/)
Now, long story short, this script, which I run in my head tag, creates a <ul> with <li>s containing <a>s. Also in the head tag it creates IDs for these <a>s. When I inspect the loaded site I can clearly see the ID tags present. However, I cannot call them using getElementById. I've been using
<script>
document.getElementById('rightpanelnav1').style.padding='200px';
</script>
as a sample script in different parts of my code but to no avail. I'm wondering wether it's the placement or order in which these things are defined in my code that's causing it not to recognize the ID. What do you think?
EDIT: I recieved a great answer below, but I still can't get 'rightpanelnav1' to register onclick events...? It's an , there shouldn't be a problem, right? And when I click it, the entire page has been loaded for several seconds...
Firstly, in order to access an element in the DOM, the element must be a part of the DOM (document). So if you place your <script> with getElementById in the page at a place prior to where the element is loaded, it will not see the element in the DOM.
Secondly, it is highly probable that this library you use does its modification on page load, which would mean that no matter where you place your <script> it would have no chance of seeing these elements before running.
As a result, you should have your script wait as well, and do this:
window.onload = function(){
document.getElementById('rightpanelnav1').style.padding='200px';
};
Or for a click event
window.onload = function(){
document.getElementById('rightpanelnav1').onclick = function(){
alert("clicked!");
};
};

How can Javascript in the head access elements in the body?

I often see some JavaScript libraries that are called in the head tag of an html page. However, if these libs call an element in the body tag, there'll be a response (still from the head tag). When I try to call an element from the head, there is no response - why?
Thanks in advance. :)
The browser parses the html page from top to bottom, executing any <script> blocks in place as it finds them. Which means if the JavaScript attempts to access elements on the page it can only see those included higher in the page because the ones lower down have not been parsed yet.
There are two ways to deal with this:
Put your <script> block at the bottom, just before the closing </body> tag (or at least put it after the elements it needs to reference), and/or
Use an onload (or, if you like jQuery, a $(document).ready()) handler.
One way to setup an onload handler is like this:
<head>
<script>
window.onload = function() {
// this function will be called by the browser after
// the entire page has loaded and thus code in the function
// can access any element on the page.
};
</script>
</head>
<body>
... various elements ...
</body>
You may have also seen something like this:
<body onload="someFunction();">
Where someFunction() can be defined in a <script> block in the <head>. It's basically the same thing, but it's so 1990s to do it with an attribute in the html like that. (Actually even the window.onload() is out of date now, but it works and I don't have time to explain the .addEventListener() method.)
That's beacause you propably are trying to call elements before they exist, ie. the HTML isn't parsed yet. To avoid this, you have to wait untill the page is completed, and then execute your script.
To achieve this, you have to assign your function into a onload-event. For example:
<body onload="your_func">

How to insert an element at the current position in HTML using prototype

How do you insert an HTML element dynamically (using prototype) at the current position if you don't know the id of the parent element? All examples I've seen assumes the element has some kind of id.
I.e.
<script>
function addSomeHtmlAtCurrentPosition()
{
current_element = ...; // Magic?
current_element.insert(...);
}
</script>
<div>
<script>
addSomeHtmlAtCurrentPosition();
</script>
<!-- should insert something here... -->
</div>
<div>
<script>
addSomeHtmlAtCurrentPosition();
</script>
<!-- ... and then here -->
</div>
I've tried using prototype with $$('div').last(), but that doesn't seem to work (last() sometimes reports back another div if I use libraries such as livepipe).
What I really want is something similar to document.write, but using prototype instead of raw html.
The only way I can think of is finding the <script> element and insert the element before/after the script. The thing is, all DOM methods need a DOM node to operate on. Executing scripts that modify the DOM before the document has loaded isn't a good and safe idea.
Similar questions linked below.
JavaScript: get the current executing <script> node?
How may I reference the script tag that loaded the currently-executing script?

Can I just put my javascript right next to my <div>

If I have some javascript/jQuery acting on a particular div inside tags (for an animation), can I just put the javascript (in a src="link-to-my-js.js" file) right next to my div?
I mean something like the following:
<body>
<div id="special">Some html</div>
<script type="text/javascript">javascript related to my div tag above...</script>
</body>
whereever you add your code wrap it with document .ready.
It will wait till all the dom is ready, so you will be safe.
<script type="text/javascript">
$(document).ready(function() {
});
</script>
You can put them anywhere you want, but
For performance reasons, it's best to put them at the bottom of the page.
DOM manipulation before the page has loaded can cause problems, especially with IE, for example this google maps problem.
You can add the tag anywhere you please. Remember though that if your script tries to act on an element that has not yet been loaded it will fail.
So if your load your script tag before your div and the script isn't activated by an onload event or something similar element will not be found.
On the contrary if the tag appears after the element you can manipulate it as normal.
It does not matter where you put it if you wrap your code with the $(function(){/*code here*/})
reference: http://api.jquery.com/jquery/#jQuery3

Categories

Resources