Three JavaScript articles and one best practice

It's getting busy on the JavaScript front. For a good overview of what's happening right now you should read the three articles I mention below. They discuss different aspects of the change JavaScript is going through at the moment. As an extra I've thrown in a little trick I've been using quite a lot lately.

Changes to JavaScript

First of all Brendan Eich, inventor of JavaScript, speaks up. In his JavaScript 1, 2, and in between he discusses possible directions JavaScript could evolve in:

JS is not going away, so it ought to evolve. [...] You could argue that JS's stagnation, along with HTML's, was beneficial for the "Web 1.0" build-out of the last decade. But given all the ferment on the web today, [...] there should be a JS2, and even a JS1.6 on the way toward JS2.

Eich discusses a few Core features of JavaScript that should change. I'm the first to admit I don't understand Core JavaScript enough to judge these proposals at their worth. Therefore I gladly leave these changes in Eich's capable hands.

Unobtrusive scripting

Three weeks ago Bobby van der Sluis published Unobtrusive dynamic select boxes, a simple way of creating dynamic select boxes without excluding noscript browsers — but remember: catering for noscript browsers does not make your site accessible in and of itself. Nonetheless Van der Sluis's script is an excellent example of the direction JavaScript as used by us web developers should be traveling in.

If you're not quite sure what unobtrusive scripting means, read this article carefully. Van der Sluis starts with the structural XHTML layer and then explains how to add an unobtrusive behaviour to this layer. Since he first made sure his structure is correct, usable and accessible, the removal of the behaviour only results in the loss of a bit of usability, but leaves the page otherwise intact.

Bad practices

Chris Heilmann put together an interesting list of bad practices in Six JavaScript features we do not need any longer. The list is:

  1. document.write (actually, there is one situation in which it remains useful; see below)
  2. <noscript>
  3. the javascript: pseudo-protocol
  4. onclick="void(0)" (I must admit I never saw this one in the wild)
  5. document.layers, document.all, and navigator.userAgent
  6. inline event handlers

I completely agree with him: we should get rid of these bad practices as fast as possible. There's one exception, though.

The single correct use of document.write

I have rediscovered document.write as a powerful tool in one specific situation: when you want to include styles that should only work if JavaScript is enabled.

  1. Take a show/hide navigation like my navigation frame. Initially all blocks with links are hidden, and they open only if the user clicks on the appropriate label. A nice bit of unobtrusive JavaScript.
  2. However, to make certain it's unobtrusive we should add the commands that hide the blocks of links only when JavaScript is enabled. If we don't, noscript browsers cannot access the links in any way. Bad accessibility.
  3. The traditional solution to this problem is to close the blocks when the script kicks in by a style.display = 'none'. Thus the blocks are closed by JavaScript; if there's no JavaScript they remain open.
  4. The problem with this approach is that it might take too long: the blocks are closed when the script kicks in, which is onload. If the user first has to download huge lots of graphics, the blocks may 'suddenly' close while the user thought the page was already complete. Bad usability.
  5. My solution is to document.write an extra <link> tag into the page directly (ie. outside any function), which links to avanced styles that should only be used when JavaScript is enabled. This has three advantages:
    1. The document.writeing itself makes sure these styles are only loaded when there is JavaScript support.
    2. Since we add the <link> as soon as possible, and don't wait for onload, the styles are there when the browser starts parsing the XHTML. No more ugly flickers or movement: all XHTML elements immediately get the advanced styles they need. The blocks are never open, so they don't need to close suddenly.
    3. You can keep presentation and behaviour separate: all CSS instructions go into the extra style sheet.

Example script; note that the <link> is written into the page as soon as possible, it doesn't wait for onload:

var W3CDOM = (document.getElementsByTagName && document.createElement);

if (W3CDOM)
	document.write('<link rel="stylesheet" href="advanced.css" />');

window.onload = function () {
	if (!W3CDOM) return;
	// start up W3C DOM scripts
}

In my opinion this is the single situation in which document.write is an asset rather than a liability.

This is the blog of Peter-Paul Koch, mobile platform strategist, consultant, and trainer. You can also follow him on Twitter.
Atom RSS

I’m around at the following conferences:

(Data from Lanyrd)

Categories:

Monthlies:

Comments

Comments are closed.

1 Posted by Chris Hester on 22 June 2005 | Permalink

What on earth's wrong with noscript? I use it to tell the user part of my page requires JavaScript. It can also be used to supply extra styles and content for non-script users. Besides, the W3C recommend using it, don't they?

2 Posted by Chris Heilmann on 22 June 2005 | Permalink

Dang, you are right ;)

3 Posted by Robert Nyman on 22 June 2005 | Permalink

First, I don't think that your use of document.write is a good idea.
Use the DOM, man!

if (W3CDOM) document.getElementById("menuStyle").setAttribute("href", "advanced.css");

Second of all, I'd also recommend reading http://www.robertnyman.com/2005/06/20/rise-lord-javascript/ about JavaScript awareness.

4 Posted by Robert Nyman on 22 June 2005 | Permalink

Your system apparently stripped away my LINK tag in my previous comment, but of course there should be a LINK tag with an id of "menuStyle" and no HREF for that script to work properly.

5 Posted by Adam Taylor on 22 June 2005 | Permalink

Robert - won't that fail to work, 'cos it can't scan the DOM until the page loads, which is the whole problem in the first place?

6 Posted by 4rn0 on 22 June 2005 | Permalink

In regards to the noscript tag. Of course we prefer unobtrusive, well written JavaScript. That's what we should definitely aim for and that's why we don't need any noscript 'Your browser sucks, go and upgrade it!' messages.

However, it seems to me that it is equally desirable to inform our less-than-fortunate, JavaScript-less visitor that he or she is not experiencing the full potential of our well coded, handcrafted website. And THAT'S where the noscript tag comes in...

7 Posted by James Ojaste on 22 June 2005 | Permalink

The theory behind not needing the "noscript" tag is that you can simply use JS to hide a block of text that's displayed by default. Of course, it's subject to the same timing issues that ppk described with document.write.

I didn't see any reason why DOM manipulation should have to wait for the entire document, so I threw together a quick test. I put in a quick 2-liner (var n = document.getElementById("noscript"); n.parentNode.removeChild (n);) in a div followed by 20M of text. Worked like a charm in FF 1.0.4, but IE 6.0.2800 throws up an error message: "Internet Explorer cannot open the Internet site http://servername/testscript.html. Operation aborted". Guess IE didn't like the script removing itself.

You can still work around that problem by setting a base style of "#noscript { display:none; }" followed by your id="noscript" div, followed by JS to remove the id="noscript" div, finally followed by a style of "#noscript { display:block; }". The disadvantage to this, of course, is that if you have a really long block of text, it won't appear until after the 2nd style is downloaded.

8 Posted by Tino Zijdel on 22 June 2005 | Permalink

There is also no need to use document.write for the case mentioned in this article since the DOM is already present and accessible through javascript during rendering.
See my example page: http://therealcrisp.xs4all.nl/meuk/jsappendedstylesheet.html

I often use inline javascript to be executed right after the rendering of certain elements to immediately perform javascript functions on them. Althoug it is not a clean separation of markup and behavior it beats the downside of waiting for onload.

document.write is however certainly not dead yet; bannerproviders use it to generate bannercontent - it's the easiest way.
A lot of site also still use it to reduce bandwidth; huge chunks of markup go into cacheable scriptfiles and only the content wrapped in functioncalls is sent to the client. Offcourse this is legacy from the time where not all browsers could handle gzipped content that well (IE still has bugs on that point), but it is still very common.

9 Posted by Robert Nyman on 22 June 2005 | Permalink

Adam,

As Tino pointed out with his example page, it works fine manipulating the DOM before the whole page has loaded.

When it comes to ad content, wouldn't it be possible for ad providers to tell the web developer that's going to incorporate their ads to set a string variable with the ID of the element where they'd like the ad content to be added (the name of the variable should be specified by the ad provider)?

Then the ad provider's script could be proper DOM scripting and instead use appendChild to insert their ads to the element identified through above mentioned variable.

10 Posted by Tino Zijdel on 23 June 2005 | Permalink

Robert: as for advertising using document.write() it's the management tools like phpAdsNew that will need to change to accomodate that. At the moment when you insert a new ad (usually by uploading an image or flash movie, or inserting some HTML) the management tool will generate the necessary javascript for sites that request the ad in javascript format. Generally these tools wrap the content in document.write()'s so they can be 'inserted' on the requesting page at the spot of the JS call.

I don't expect a change in that corner anywhere soon (unless IE7 would by default block all javascript from another domain).

11 Posted by Tino Zijdel on 23 June 2005 | Permalink

By the way: the article from Bobby van der Sluis has a good example of necessary use of the navigator.userAgent property. I too had to resort to that recently to work around a JS-related bug in Opera 8

12 Posted by Robert Nyman on 23 June 2005 | Permalink

Tino,

Thanks for the info, I didn't know about phpAdsNew. I'm also a bit curious about Google AdSense and others, but my hope is (as every other web developer's) that they will update their products to instead use DOM scripting soon in the future.

13 Posted by Jim Ley on 23 June 2005 | Permalink

the write method of the document object is completely standard, now there are good arguments for it not to be used, but that it's not standard is not one of them see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-html.html .

14 Posted by Littlecharva on 23 June 2005 | Permalink

What's wrong with using href="javascript:myFunction();"?

What should I use instead?

href="#" onClick="myFunction();"?

15 Posted by 4rn0 on 23 June 2005 | Permalink

You should add an ID to the anchor and add an onclick behaviour to it through an external JavaScript file!

href="#" id="myId"
document.getElementById('myId').onclick = myFunction;

(or even better, use a wrapper function that incorporates addEventListener and attachEvent)

16 Posted by Littlecharva on 23 June 2005 | Permalink

Can you point me in the direction of an example wrapper function?

17 Posted by hemebond on 23 June 2005 | Permalink

Always put proper URL's in the HREF attribute. If it shouldn't have a URL, don't make it a link.

18 Posted by Tom Clancy on 23 June 2005 | Permalink

"Can you point me in the direction of an example wrapper function?"

http://www.scottandrew.com/weblog/articles/cbs-events

19 Posted by Joost on 23 June 2005 | Permalink

noscript is still needed when select boxes auto-submit a form onchange(). inside the noscript tags, the submit button is shown.

20 Posted by Robert Nyman on 23 June 2005 | Permalink

Joost,

An alternative approach might be to first include the button in the HTML code, and then use unobtrusive JavaScript to hide/remove it for those who have JavaScript enabled.

21 Posted by Joost Diepenmaat on 23 June 2005 | Permalink

The biggest problem I can see with is that it doesn't help you with the cases when don't have "enough" javascript support (like no XMLHttpRequest, for instance). With the difference in browsers today, you'd have to test the available features anyway, which makes the tag sort of useless.

22 Posted by Fuzztrek on 24 June 2005 | Permalink

"noscript is still needed when select boxes auto-submit a form onchange(). inside the noscript tags, the submit button is shown."

One could question the accessibility and indeed usability of such a "solution". I personally hate anything that automatically submits a form for me, even though I do not have any accessibility concerns myself - and, of course, using this for navigation is not an option. Perhaps there are situations where this is not annoying, though.

As for the "bad practices", I mostly agree. However, I don't see going anywhere until JavaScript is completely supported -or- an appropriate alternative is suggested. I use it quite often, *especially* when validating forms serverside - my errors are hidden via css; a class is added to errors in JavaScript to show them; if JavaScript is not available, the form is submited and the appropriate CSS to show the errors is written in a noscript tag (other errors that I cannot check with javascript - database issues, etc. are placed in an array that my error showing function checks on page load).

This seems to work pretty well but I think it would get pretty ugly without the noscript tag. Open to suggestions of course. It really makes you realize how primitive HTML forms are - clientside AND serverside validation, oi!

23 Posted by Fuzztrek on 24 June 2005 | Permalink

"However, I don't see going anywhere until JavaScript is completely supported -or- an appropriate alternative is suggested."

hmm. that was supposed to be:

"However, I don't see noscript going anywhere until JavaScript is completely supported -or- an appropriate alternative is suggested."

24 Posted by Gerv on 24 June 2005 | Permalink

I think it's possible to avoid PPK's single remaining use of document.write(). Get some script which executes immediately (i.e. not onload) to add a style rule to the style DOM which hides the class you've given to the link blocks. Then, they'll get hidden as soon as they are rendered.

I'm fairly sure adding style rules is supported across all modern browsers, albeit perhaps with different syntax.

25 Posted by randfish on 27 June 2005 | Permalink

It's incredible to see such debate over this issue. I apprecaite all the hard work you've put into educating the rest of us and I hope to see more good advice in the future. Also - thanks for the link Tom C.

26 Posted by ppk on 28 June 2005 | Permalink

Great to see such a high-level discussion instead of the usual boring comments!

I'm not going to take any decisions now, but a few good arguments have been advanced, and I'll study them when the time comes to define bad practices.

If you have anything more to say, please continue the discussion.