views:

434

answers:

4

I have a long-running report and want to show a wait-spinner to the user while it's generating. I have made this work already but am not sure I'm doing it the best or right way.

This is using ColdFusion but it could be any language I guess. At the top of the page, I have some Javascript (jQuery) that shows a wait-spinner plus there's a documentReady handler where I take the spinner down. I flush the output ( if it matters) and then the rest of the code works on the report contents. This wasn't ever rendering the spinner and I theorized that, even though I was flushing things on the server, some buffering was happening along the way and the browser never saw the spinner code until too late. So, I added a loop right before I flushed that spit out a few hundred lines of HTML comments. After fine-tuning the number of lines, that did the trick. I assumed then that that's how other sites did it too.

But: Today, while watching a different page of mine that spits out a line by line status of a long-running job, it occurred to me that that page flushes after each line and the browser renders that incrementally as desired. This doesn't match my conclusion from above and now I don't know what the rules are. Is there a predictable way to do this? Does it vary per browser?

CLARIFICATION: I appreciate the answers that attempt to explain the correct way to do a wait-spinner but I'm just using a wait-spinner as an example to illustrate my real question: are there reliable ways to predict when browsers will start to render HTML as it is streamed to them over the net? It's obvious through observation that browsers don't wait for the /html tag to start work. This question doesn't necessarily have anything to do with Javascript. For instance, that second page I describe that shows status is pure HTML.

A: 

I expect it will vary by browser: some will start to render before the page is completely received; others will wait for the whole html file before starting.

I note that the HTTP Response specification provides a "206 Partial Content" header which might offer some hope for a cross-browser solution which is worth looking into.

There's almost certainly a number of ways to cut it, but my immediate idea would be to deliver the stub page, then use ajax to pull and render the fragments on demand. eg a lot of big sites seem to be delivering a minimal view initally, then if you scroll down enough it fires an ajax request to retrieve more data and append it to the page. eg: facebook, slashdot.

I can't imagine this being too hard to implement; even with coldfusion. You start the spinner when you fire the ajax request and stop it when the callback is invoked.

John Mee
+2  A: 

--- Post-clarification Answer ---

My original answer should be useful to someone (I hope), but it isn't a direct response to the question, so I'll post another answer

The answer to your restated question is "no". In the case of FF, there is a predefined initial render delay, but other browsers will be different. That FF render delay can be tweaked as well.

Using FF as an example, those initial 250ms are time for FF to find out at least some useful information before attempting the first render. It then does additional renders periodically as it learns more.

There is no way for you to determine when the browser has started to render the HTML document.

--- Original Answer ---

To directly answer your question, I believe Firefox waits 250ms before acting on the first data received, but that can be changed. For other browsers, I don't know.

However, you don't want to go that route.

When jQuery is ready to work its magic, it'll let you know by firing $(document).ready(). Before then, any attempt to use jQuery will fail. In other words, your spinner isn't showing up because jQuery isn't ready to process that request yet.

Consider the following example, where two placeholders are shown on-screen, and we'll use jQuery to hide them.

<html>
    <head>
        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.min.js"&gt;&lt;/script&gt;
        <script>

            // We're not ready yet.  This will fail.
            $(".placeholder").hide();

            $(document).ready(function(){
                // This won't fail
                $("#one").hide();
            });

        </script>
    </head>
    <body>
        <div id="one" class="placeholder">Placeholder One</div>
        <div id="two" class="placeholder">Placeholder Two</div>
    </body>
</html>

It may appear at first that $("#one").hide(); is redundant, but that's not true. $(".placeholder").hide(); is called before jQuery is ready, so it has no effect, which is why you'll see "Placeholder Two" displayed (and not "Placeholder One") if you run the markup above in a web browser.

Now that we have that out of the way, the solution to the larger problem (the "right way") is AJAX.

  1. Load a base page that contains the spinner code. Make sure the code to load the spinner runs as part of $(document).ready().
  2. Use jQuery's AJAX functionality to get the report you want.
  3. When it comes back, hide the spinner, inject the report into your base page.

Good luck!

E-man
E-man - Where did you get the 250ms number? You're the second person to say that what I'm doing is not what I want to do. Ok, but why? Is it unreliable? This is an in-house app where using FF is required so I'm not worried about other browsers (but would like to know that too). I'm aware of the AJAX approach but was trying to avoid the extra complexity.About your example - I think you're saying that the initial hide() won't work because jQuery isn't ready but my current code works fine and does so by invoking jQuery before document.ready fires. Am I missing something?
DaveBurns
For the 250ms http://hackaday.com/2004/12/26/speed-up-firefox/ You can't use jQuery before $(document).ready() fires-- I think your comment loop is forcing a "ok something is stalled, let's take what we've got for now" situation in FF (best guess). It isn't normal, I'm sure of that.I'll answer the restated question in another Answer.
E-man
+2  A: 

If I understand you correctly, you want a "loading"-image to display whilst page is still loading up content.

What I do, and what I believe is best way is to add a div-tag at top of your page, closest to body-tag which holds the loading image/text. You can place this div elsewhere with help of some css.

Then have Jquery remove this div when page is loaded. Instead of using $(document).ready as some have recommended i would use $(window).load instead, since it is activated when the complete page is fully loaded, including all frames, objects and images.
Se link here; http://4loc.wordpress.com/2009/04/28/documentready-vs-windowload/

Example;

<body>    
<div id="loading" style="z-index:200; width:100%; height:40px; border-top:2px #D4D4D4 solid;  background:#E8E8E8; color:#000; position:fixed; bottom:0; left:0;">
    <div align="center">
        <img src="load.gif" alt="Loading...">
    </div>
</div>
<script type="text/javascript">
    $(window).load(function() { $("#loading").remove(); });
</script>
[...]

...

jamietelin
+1  A: 

Other than the built in delay for Firefox, there are other areas which cause the browser to wait before rendering.

As an html page is sent to the browser it first has to decide where things go before it can draw the screen. For example, tables are notorious for causing rendering delays. In order to draw a table the browser needs to compute column sizes. If you use percentages as column widths or don't specify them at all, then the browser has to get the entire table before rendering.

However, if you use something like the table-layout: fixed; css attribute then the browser can just read the first row and start drawing as data is fed to it.

Other tags can cause similar issues. Basically any time the browser has to compute the size of content, then it has to have all of the content to render. If you can give it hints ahead of time through the use of fixed size (width/height) elements then the browser doesn't need to figure anything out and it can draw the elements as they are downloaded.

As a self-imposed rule, I never let the browser determine anything. Instead I give my elements fixed sizes. As a result the sites I work on usually have lightning fast rendering.

Chris Lively