views:

352

answers:

4

This is generally how I manage progressive enhancement whilst keep the experience clean, but how safe is it? is there potential for a race condition and this not working?

Imagine the simple abstract scenario, you want to display something differently if you have javascript support.. this is generally what I will end up doing:

<div id="test">original</div>
<script type="text/javascript">
    var t = document.getElementById('test');
    t.innerHTML = 'changed';
</script>

Many may claim you should use a framework and wait for a domready event, and do changes there.. however there is a significant delay where the 'test' element will have already been rendered before the end of the document and the css are ready and a domready triggers.. thus causing a noticable flicker of 'original'.

Is this code liable to race condition failures? or can I guarentee that an element is discoverable and modifiable if it exists before the script?

Thanks in advance.

+3  A: 

You can manipulate the DOM before it has fully loaded, but it can be risky. You obviously can't guarantee that the bit of the DOM you are trying to manipulate actually exists yet, so your code may fail intermittently.

rikh
Thanks rikh, what do you mean theres no guarantee, by specification or by actual reality of products that exist today (ff, ie, opera etc).. I would think the parser acts linearly and adds elements as it encounters their closing node.. whilst this may not be a specification, I would think this is a sensible behavior, and was more looking to find authoritize answers that this is the case.
meandmycode
To be honest, I don't know, but it is not something I would want to depend on. If you are really worried about the wrong information being displayed, try displaying nothing initially, and then filling in something on doc ready, or simply design the problem out of the system, so you don't need to do what you are asking.
rikh
Unfortunately thats not possible, the last example I have is where we render out pagination numbers as hyperlinks, and our javascript runs and changes these so we can display 'Page x of y' and highlight the page hyperlink that is active (the pages are anchors).. this is a typical progressive enhancement scenario where we render out something that will work without javascript.. and then intercept and modify it with javascript in order to use javascript.. the fact you can manipulate it with javascript then proves you.. have javascript support ;)
meandmycode
+3  A: 

You can, but there are issues surrounding doing it.

First off, in IE if you attempt to manipulate a node that has not been closed (e.g. BODY before its close tag which should be below your JS) then you can encounter IE's "OPERATION ABORTED" error which will result in a blank page. Manipulation of a node includes appending nodes, moving nodes, etc.

In other browsers the behavior is undefined, however they do usually behave as you would expect. The main issue is that as your page evolves, the page may load/parse/run differently. This may cause some script to run before a browser defines referenced elements have actually been created and made available for DOM manipulation.

If you are attempting to make enhance your user perceived performance (i.e. snappiness). I highly suggest that you avoid this path and look into lightening your pages. You can use Yahoo's YSlow/Google's Page Performance Firebug to help you get started.

Google's Page Speed

Yahoo's YSlow

Raegx
Hi Raegx, yes I can completely understand the operation aborted issue with changing elements that havent been closed.. in terms of the snappiness, these pages are light, the actual page is 15kb.. however my changed element may well be right under the opening body tag, and it WILL render before domready fires, regardless of how light my page is.. I'm trying to avoid a sloppy experience that is always experienced imo with domready and doing these kinds of things.
meandmycode
Back in the dark ages, when we didn't have a cross-browser way to fire onDomReady we used to use onLoad, which fired after all images/css/etc was loaded and rendered. Now we have onDomReady which fires after the DOM has been parsed and represented, but potentially before images/etc. This is a relatively new advancement too. So waiting for OnDomReady is all we have at this point and its the best solution for robustness.If you believe that your manipulation before onDomReady is worth it and you aren't appending/creating/moving nodes to unclosed nodes you are probably fine.
Raegx
Great, its certainly worth it if it works.. from what I'm hearing, it should work fine but theres nothing officially stating that an element should be ready after its closing node.
meandmycode
+1  A: 

As long as you only modify nodes which preceed the script block (ie the node's closing tag preceeds the script's opening tag), you shouldn't encounter any problems.

If you want to make sure the operation succeeds, wrap the code in a try...catch block and call it again via setTimeout() on failure.

Christoph
Thanks Christoph, I was considering that upon failure I would get nothing back or an exception and could retry.. although I'm not fond of such retry logic, I worry that the execution may hinder the page loading performance.. I'm going to see if I can run some tests by trying to simulate highly constrained bandwidth and even low processing power to see if I can find any window at all that exists.. so far bandwidth doesnt appear to do anything, I've been running iterations of the page loading at 100 bytes a second (it takes awhile).. and by the time the area has loaded it has already ...
meandmycode
.. executed the script perfectly fine.. I imagine constraining processing time is much more likely to cripple the parser and illuminate any windows where the assumption is wrong.
meandmycode
A: 

In Viajeros.com I have a loading indicator working since 8-9 months and I have no problems so far. It looks like this:

<body>

<script type="text/javascript">
 try {
  document.write('<div id="cargando"><p>Cargando...<\/p><\/div>');
  document.getElementById("cargando").style.display = "block";
 } catch(E) {};
</script>
Danita
Maybe that isn't all the code, but you do realize that the `getElementById` is completely unnecessary, right? You can just add `style="display:block"` to the HTML.
DisgruntledGoat