4

When a page loads on my site, the HTML appears before the javascript, which leads to a flicker when the javascript loads. The answer to this stackoverflow post gave a great solution. But I would like to load at least some of the HTML before the Javascript so that the user is not faced with a blank page during a slow connection. For example, I would like to load the header immediately, but wait to load the HTML for the javascript enhanced accordion until after the javascript loads. Any suggestions?

Here's the code that I borrowed from the answer linked above:

CSS:

#hideAll
 {
   position: fixed;
   left: 0px; 
   right: 0px; 
   top: 0px; 
   bottom: 0px; 
   background-color: white;
   z-index: 99; /* Higher than anything else in the document */

 }

HTML:

<div style="display: none" id="hideAll">&nbsp;</div>

Javascript

 window.onload = function() 
     { document.getElementById("hideAll").style.display = "none"; }

<script type="text/javascript">
   document.getElementById("hideAll").style.display = "block";
 </script> 
Community
  • 1
  • 1
Ken
  • 3,091
  • 12
  • 42
  • 69
  • so what's wrong with using onload? it will run your script when the page is loaded – Ibu May 25 '11 at 04:23
  • @Ibu: `onload` doesn't fire until *everything* --including images--have loaded. This could be a very long delay on a slow connection. The appropriate event to use for this kind of document manipulation is [DOM ready](http://www.javascriptkit.com/dhtmltutors/domready.shtml). – josh3736 May 25 '11 at 04:35

2 Answers2

3

I'd suggest that you define the base/JavaScript-enabled styles of elements you want to display with CSS in the regular style block:

<style type="text/css">
    #javaScriptAccordion {
        display: none;
    }
</style>

And then use the noscript tags (in the head) to amend this in the absence of JavaScript:

<noscript>
    <style type="text/css>
        #javaScriptAccordion {
            display: block;
        }
    </style>
</noscript>

This ensures that the content is hidden on document load, preventing the flash, but visible to those users that have JavaScript disabled.

The above has been amended to prevent the 'flash of no content' (as described by @Josh3736 in his answer), and now uses opacity to hide the content:

<style type="text/css">
#elementToShowWithJavaScript {
    opacity: 0.001;
    width: 50%;
    margin: 0 auto;
    padding: 0.5em;
    border-radius: 1em 0;
    border: 5px solid #ccc;
    }
</style>
<noscript>
<style type="text/css">
    #elementToShowWithJavaScript {
        opacity: 1;
        }
</style>
</noscript>

Live demo.

I'm not, unfortunately, entirely sure that I understand your question. Which leaves me proposing a solution for the question I think you asked (all I can offer, in excuse, is that it's early in the UK. And I'm not awake by choice...sigh); if there is anything further that I'm missing (or I'm answering the wrong question entirely) please leave a comment, and I'll try to be more useful.

Community
  • 1
  • 1
David Thomas
  • 249,100
  • 51
  • 377
  • 410
  • @David Thomas Thank you. It sounds like you understood my question. But, in short, I'd like to prevent the javascript-affected HTML from loading until after the javascript has loaded, while allowing the HTML not affected by javascript to load asap (e.g., the header, etc.). I'm just trying to see if I can implement your solution before asking for more help... – Ken May 25 '11 at 04:19
  • @Ken: oh...for some reason I assumed that by 'loading' you meant 'displaying.' HTML doesn't, natively, allow for selective content loading; it's either *there*, or it *isn't*. – David Thomas May 25 '11 at 04:21
  • @David Thomas I'm essentially just trying to avoid the flicker that occurs when the HTML displays before the Javascript loads. I apologize for using the wrong terms. It's late here, and I'm a novice - bad combination, I suppose. – Ken May 25 '11 at 04:51
  • @Ken, it's early here and I'm tired. So...a particularly unfortunate combination all ways around, really... =) But no worries. I've amended the demonstration to use `opacity` rather than `display` to hide the content to show. Which prevents the "flash of no content" [described by @josh3736](http://stackoverflow.com/questions/6119171/how-to-delay-the-display-of-some-html-until-after-javascript-has-loaded/6119569#6119569). – David Thomas May 25 '11 at 04:53
  • @David: It is more appropriate to use `visibility:hidden;`, which does not collapse the content block like `display:none;` and does not bump in to IE's lack of support for `opacity`. However, (regardless of whether you use `opacity` or `visibility`) this approach *still* leads to a flash of content. For example, if each accordion pane is 200px, with 5 panes, that's 1000px of empty space taken up by the accordion. When the script runs and hides 4 of those 5 panes, the accordion `
    ` will now be only 200px, making the rest of the page jump up 800px, which is a nasty flash.
    – josh3736 May 25 '11 at 05:10
  • @Josh, I agree with you as regards the `visibility:hidden;` option, but I couldn't think of a way to animate from `hidden` to `visible`. As to the reduction in size, from 1000px to 200px, that can be addessed by using the appropriate css selectors (in most cases), but without seeing the actual mark-up (or a live demo) it's difficult to advise specifically on that aspect. – David Thomas May 25 '11 at 05:14
  • I really appreciate your help with this. Am I understanding correctly that I should place the initial style for the element to show with javascript in the head, and then place the opacity:1 style within the body? – Ken May 25 '11 at 05:48
  • No, the `style` blocks have to be in the `head`, even if it's wrapped in a `noscript` tag. In the linked demo I posted look at the source. – David Thomas May 25 '11 at 06:22
  • @David: `$('#accordion').css({ opacity: 0, visibility: 'visible' }).show('fast');` As far as the selectors, you're right, assuming you can know at 'compile time' which pane will be shown when the page is loaded. If, for example, a query string/hash parameter is used to select the opening pane (which is something [you should do](http://benalman.com/code/projects/jquery-bbq/examples/fragment-jquery-ui-tabs/)), pure CSS won't cut it. – josh3736 May 25 '11 at 14:41
0

The hack in the linked question is—in my opinion—very poor advice. In this case, it is a better idea to include some script directly following your accordion elements.

<div id="accordion">...</div>
<script type="text/javascript">...</script>

However, inline script intermingled with your HTML markup is a Bad Idea and should be avoided as much as possible. For that reason, it is ideal to include inline only a function call to a function declared in your external script file. (When you reference an external script (<script src="...">), the rendering of your page will pause until it has loaded.)

<html>
<head>
<script type="text/javascript" src="script.js"></script> <!-- renderAccordion() defined in this file -->
</head>
<body>
...
<div id="accordion">...</div>
<script type="text/javascript">renderAccordion();</script>
...
</body>
</html>

Of course, the correct way to do this is to just attach to the DOM ready event from script.js and not use any inline script at all. This does, however, open up the possibility of a content flash on extremely slow connections and/or very large documents where downloading all of the HTML itself takes several seconds. It is, however, a much cleaner approach – your script is guaranteed to be loaded before anything is rendered; the only question is how long it takes for DOM ready. Using jQuery, in script.js:

$(document).ready(function() {
    // Do whatever with your accordion here -- this is guaranteed to execute
    // after the DOM is completely loaded, so the fact that this script is
    // referenced from your document's <head> does not matter.
});

Clever use of <style> and <noscript> does a a good job of guaranteeing that there is no flash of all the content in your accordion; however, with that method there will be the opposite effect – there will be a flash of no content.

As the page loads, your accordion will be completely hidden (display:none;), then once your script finally executes and sets display back to block, the accordion will suddenly materialize and push down everything below it. This may or may not be acceptable – there won't be as much movement, but things will still have to jump after they've initially rendered.

At any rate, don't wait until onload to render your accordion. onload doesn't fire until everything—including all images— have fully loaded. There's no reason to wait for images to load; you want to render your accordion as soon as possible.

Community
  • 1
  • 1
josh3736
  • 139,160
  • 33
  • 216
  • 263