24 ways

to impress your friends

Hide And Seek in The Head by Peter-Paul Koch

If you want your JavaScript-enhanced pages to remain accessible and understandable to scripted and noscript users alike, you have to think before you code. Which functionalities are required (ie. should work without JavaScript)? Which ones are merely nice-to-have (ie. can be scripted)? You should only start creating the site when you’ve taken these decisions.

Special HTML elements

Once you have a clear idea of what will work with and without JavaScript, you’ll likely find that you need a few HTML elements for the noscript version only.

Take this example: A form has a nifty bit of Ajax that automatically and silently sends a request once the user enters something in a form field. However, in order to preserve accessibility, the user should also be able to submit the form normally. So the form should have a submit button in noscript browsers, but not when the browser supports sufficient JavaScript.

Since the button is meant for noscript browsers, it must be hard-coded in the HTML:

  1. <input type="submit" value="Submit form" id="noScriptButton" />
  2. Source: /code/hide-and-seek-in-the-head/input.txt

When JavaScript is supported, it should be removed:

  1. var checkJS = [check JavaScript support];
  2. window.onload = function () {
  3. if (!checkJS) return;
  4. document.getElementById('noScriptButton').style.display = 'none';
  5. }
  6. Source: /code/hide-and-seek-in-the-head/checkjs.txt

Problem: the load event

Although this will likely work fine in your testing environment, it’s not completely correct. What if a user with a modern, JavaScript-capable browser visits your page, but has to wait for a huge graphic to load? The load event fires only after all assets, including images, have been loaded. So this user will first see a submit button, but then all of a sudden it’s removed. That’s potentially confusing.

Fortunately there’s a simple solution: play a bit of hide and seek in the <head>:

  1. var checkJS = [check JavaScript support];
  2.  
  3. if (checkJS) {
  4. document.write('<style>#noScriptButton{display: none}</style>');
  5. }
  6. Source: /code/hide-and-seek-in-the-head/documentwrite.txt

First, check if the browser supports enough JavaScript. If it does, document.write an extra <style> element that hides the button.

The difference with the previous technique is that the document.write command is outside any function, and is therefore executed while the JavaScript is being parsed. Thus, the #noScriptButton{display: none} rule is written into the document before the actual HTML is received.

That’s exactly what we want. If the rule is already present at the moment the HTML for the submit button is received and parsed, the button is hidden immediately. Even if the user (and the load event) have to wait for a huge image, the button is already hidden, and both scripted and noscript users see the interface they need, without any potentially confusing flashes of useless content.

In general, if you want to hide content that’s not relevant to scripted users, give the hide command in CSS, and make sure it’s given before the HTML element is loaded and parsed.

Alternative

Some people won’t like to use document.write. They could also add an empty <link /> element to the <head> and give it an href attribute once the browser’s JavaScript capabilities have been evaluated. The <link /> element is made to refer to a style sheet that contains the crucial #noScriptButton{display: none}, and everything works fine.

Important note: The script needs access to the <link />, and the only way to ensure that access is to include the empty <link /> element before your <script> tag.

About the author

Peter-Paul Koch Peter-Paul Koch (ppk for those In The Know) is a JavaScript guru residing in Amsterdam, the Netherlands. His cunning masterplan to get filthy rich consists of pimping his book, ppk on JavaScript, without which it's impossible to lead a succesful, or even happy, life.

Your comments

  1. § DannyB:

    I have found using Dean Edwards solutions work beautifully for me … so fast … doesn’t wait on images … i use this almost exclusively now.

    The window.onload Problem – Solved!
    window.onload

  2. § kentaromiura:

    in alternative you can use a function like this one:
    http://webreflection.blogspot.com/2006/11/my-domcontentloaded-final-solution.html
    or this one:
    http://dean.edwards.name/weblog/2006/06/again/

    to change the dom before the onload but after the dom is totally loaded and parsed.

    your solution works well for little change,
    but for more than a simple hide and seek using a “behaviour function” is preferible
    for a “behaviour function” I mean a function where you have all the changes(hide and seek) and the hijack (onclick..) to the dom.
    Cheers

  3. § Jake Archibald:

    I’m probably being really dim… but why shouldn’t the noscript element be used in this situation?

    Wouldn’t you get the same result by just wrapping your submit button in a noscript tag? You wouldn’t have the problems with dom content loading etc.

    Jake.

  4. § David Singleton:

    There’s still a slight problem with the two methods mentioned above, they’re still going to fire once the DOM is loaded. If the page contains a large amount of content, or the user is on a slow connection than you can still have that flicker of non-js content while the page itself is loading (not external resources).

    That is a relatively niche case, but it means the method in this article is a little more robust. It’s the way I’ve been using this to handle the hiding/altering of elements for Javascript enabled browsers, although I’ve found it easier to use document.write to create a link to another specific CSS file, which keeps things a bit tidier.

  5. § Isofarro:

    noscript is rendered when JavaScript is not disabled. It doesn’t render when JavaScript is available. So it becomes a problem when JavaScript realises that the browser does not support the necessary objects/methods to actually run the application we’ve built. So its a case of “browser not good enough” – in these cases the noscript is not rendered, so a graceful degradation is not possible if critical markup that’s needed is inside the noscript element.

    A common occurrance of this case is a browser with JavaScript enabled, that uses a third party firewall to remove suspicious javascript. The browser doesn’t know JavaScript has been stripped out – so it won’t render the noscript.

  6. § Pete:

    I would use an alternative stylesheet that has all the rules hiding elements and enable that with javascript

  7. § Ben Darlow:

    These techniques touch on something I’ve been recently puzzling over; script.aculo.us effects require that elements which are hidden/shown using so-called ‘combination effects’ that are supposed to be initially hidden cannot be hidden using a stylesheet but instead only through inline styles (I’m not sure what the exact technical reasons for this are, but sure enough if you use a stylesheet to hide them, they’ll never be shown via scriptaculous). My solution was to add an onLoad event method which manually hid all qualifying elements. The problem there is, as you say; the equivalent of a FOUC – the elements are there as the page loads, but disappear as soon as loading finishes. Really they should be hidden immediately.

    Perhaps there’s something in your tips above that might work here…

  8. § Nate K:

    I would agree with what you (and others) have said here. I would avoid using document.write and create the element with the DOM, and I would avoid explicitly setting the styles in the JS file. Have classes setup accordingly for the situations and then reference the element.className.

    Of course, this isnt NECESSARY, it just my anal attention to the small details.

  9. § Drew McLellan:

    Nate – that only works once the DOM is loaded. The problem PPK is addressing here is specifically how to prevent unwanted items displaying before the DOM is fully available.

  10. § Kabari Hendrick:

    I think some of you missed the finer points of this article. Look at Problem: the load event carefully. The difference is that you are not waiting to check if the @Window@ has loaded before you fire your script. Using @document.write@ will print your simple CSS immediately to the page, thus hiding your elements whereas using any javascript functions like @getElementsByID@ etc. will not fire until the html content of the html on the page is already loaded, even if you use them in the same way. It’s as simple as this, @document.write@ doesn’t need any html to work, alll the other functions can’t work until the html exists.

  11. § Jesse Skinner:

    To have different stylesheets for people with and without JavaScript, I like to use a single link tag pointing to a CSS file, then dynamically change the href to point at the JavaScript-only CSS file that simply imports the original CSS file and adds a few extra things.

    To see a longer explanation with an example, check out No-JavaScript CSS

Commenting is closed for this article.

24 ways: day 6