views:

107

answers:

2

I have been using this chunk of code to programmatically check if Javascript is available and use different behavioral CSS.

If Javascript is disabled, it will only show a normal unordered list.

If javascript is enabled, the list will be transformed after page load and then displayed to the user (avoiding the possible redraw/flicker of the unordered list element before/during transformation).

Is it possible that document.getElementsByTagName('body') can actually return an empty array at this stage of the page rendering, or is it always available?

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"&gt;
<html>
 <head>
  <title>Title</title>
 <style type="text/css">
  .javascript ul { display:none; }
  .no_javascript ul { display:block; }
  .transformed { background-color: yellow; }
 </style>
 </head>
 <body class="no_javascript">

<script type="text/javascript">
// Inform us that javascript is available
(function() {
    var body = document.getElementsByTagName('body');
    if ( body && body[0] ) {
        body[0].className = body[0].className.replace(/no_javascript/, "javascript");
    }
})();
</script>

 <ul>
  <li>Option 1</li>
  <li>Option 2</li>
  <li>Option 3</li>
 </ul>

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"&gt;&lt;/script&gt;
<script type="text/javascript">

$(document).ready(function() {
 $(".javascript ul").addClass("transformed").css('display','block');
});

</script>
 </body>
</html>

Update: http://stackoverflow.com/questions/463670/how-to-eliminate-post-render-flicker seems like a duplicate of my question. It was the problem I was trying to solve properly.

+1  A: 

Well... you can use document.body. It's rather safe.

Alexander Gyoshev
Is it always available?
Robin Smidsrød
in my experience, it always has been :)
Alexander Gyoshev
+2  A: 

As far as I know, in HTML scripts are parsed sequentially (unless they have defer attribute, but that's another story).

If you look at SCRIPT section in HTML4.01 specs, you can see that:

Scripts may be evaluated as a document loads to modify the contents of the document dynamically.

and that:

All SCRIPT elements are evaluated in order as the document is loaded.

There's also an example of how the following script would affect resulting markup:

 <TITLE>Test Document</TITLE>
 <SCRIPT type="text/javascript">
     document.write("<p><b>Hello World!<\/b>")
 </SCRIPT>

Has the same effect as this HTML markup:

 <TITLE>Test Document</TITLE>
 <P><B>Hello World!</B></P>

Since your script is contained right after BODY and has no DEFER attribute, it's probably safe to assume that during script execution, BODY tag is already parsed and BODY element is already created (but not the rest of the tree, such as elements created from the markup following script in question!).

And, of course, you should really use faster, more compatible and shorter document.body rather than document.getElementsByTagName('body')[0] :)

In fact, I would replace this whole chunk:

(function() {
    var body = document.getElementsByTagName('body');
    if ( body && body[0] ) {
        body[0].className = body[0].className.replace(/no_javascript/, "javascript");
    }
})();

with something like:

document.body.className = document.body.className.replace('no_javascript', 'javascript');
kangax
I already replaced my getElementsByTagName('body')[0] with document.body in my code. Thanks for the suggestion. But shouldn't the code be document.body.className = document.body.className.replace('no_javascript', 'javascript') because strings are immutable in JavaScript? AFAIK, str.replace() returns the changed string, it doesn't do in-place manipulation of the string.
Robin Smidsrød
@Robin Thanks for catching that! I changed the answer to fix this mistake.
kangax