Tapestry
  1. Tapestry
  2. TAPESTRY-2364

YSlow Recommendation: Write Scripts at bottom of page

    Details

    • Type: Improvement Improvement
    • Status: Closed
    • Priority: Minor Minor
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: 5.0.12
    • Component/s: tapestry-core
    • Labels:
      None

      Description

      Tapestry is already doing a good job on this, by writing the dynamically generated <script> block at the bottom of the page, but the external scripts should also be down there.

      http://developer.yahoo.com/performance/rules.html#js_bottom

      1. TestSlow.html
        0.9 kB
        Onno Scheffers
      2. TestSlow.html
        1 kB
        Davor Hrg
      3. TestFast.html
        0.9 kB
        Onno Scheffers
      4. sayHello.js
        0.0 kB
        Onno Scheffers

        Activity

        Hide
        Howard M. Lewis Ship added a comment -

        This doesn't make a huge difference to the YSlow score, but it does no harm either.

        Show
        Howard M. Lewis Ship added a comment - This doesn't make a huge difference to the YSlow score, but it does no harm either.
        Hide
        Onno Scheffers added a comment -

        I think the external libraries, especially the Prototype libs should always be included in the head section. Otherwise it is impossible to execute Javascript before the page renders, assuming developers are allowed to use Prototype in their own scripts as well.

        Prototype included the dom:loaded event for a reason: This feature is used a lot!
        If you want to create webpages that work without Javascript, you typically render plain HTML and then modify the HTML with Javascript before rendering, so you can give people with Javascript a better user-experience.

        When all scripts are added to the end of the page, like it is now, the page is rendered before all resources are loaded and the user sees things (s)he wasn't supposed to see (like areas collapsing etc.).

        I've seen quite a few complaints about it on the mailinglist already and some people are including the libraries twice into their pages now to work around problems. If there's hardly a difference in YSlow, then why make things more complex for developers than they need to be?

        Show
        Onno Scheffers added a comment - I think the external libraries, especially the Prototype libs should always be included in the head section. Otherwise it is impossible to execute Javascript before the page renders, assuming developers are allowed to use Prototype in their own scripts as well. Prototype included the dom:loaded event for a reason: This feature is used a lot! If you want to create webpages that work without Javascript, you typically render plain HTML and then modify the HTML with Javascript before rendering, so you can give people with Javascript a better user-experience. When all scripts are added to the end of the page, like it is now, the page is rendered before all resources are loaded and the user sees things (s)he wasn't supposed to see (like areas collapsing etc.). I've seen quite a few complaints about it on the mailinglist already and some people are including the libraries twice into their pages now to work around problems. If there's hardly a difference in YSlow, then why make things more complex for developers than they need to be?
        Hide
        Martin Strand added a comment -

        But the DOM isn't loaded any faster just because you put the script tag further up in your document, you still need to load the entire page first.

        If I understand your concern, you want your user to see a blank page while waiting for scripts in <head> to load, rather than your user seeing the full page while waiting for scripts at the bottom of the page to load? I believe that's the opposite of what most others want so it should probably not be the default behaviour.

        I believe putting scripts at the bottom so that the page renders faster is likely what most people want. If someone actually wants the page to render slower or needs to execute a script before the dom is loaded, there could be a simple workaround or a configuration option for putting scripts at the top of the page.

        Show
        Martin Strand added a comment - But the DOM isn't loaded any faster just because you put the script tag further up in your document, you still need to load the entire page first. If I understand your concern, you want your user to see a blank page while waiting for scripts in <head> to load, rather than your user seeing the full page while waiting for scripts at the bottom of the page to load? I believe that's the opposite of what most others want so it should probably not be the default behaviour. I believe putting scripts at the bottom so that the page renders faster is likely what most people want. If someone actually wants the page to render slower or needs to execute a script before the dom is loaded, there could be a simple workaround or a configuration option for putting scripts at the top of the page.
        Hide
        Davor Hrg added a comment -

        this is simply untrue.
        QUOTE: "Otherwise it is impossible to execute Javascript before the page renders..."

        A listener to onload can be added no matter where script tags are.
        onload will not be called until all page resources are loaded.

        if you use tapestry to add script to the page, the script will be called inside an onload
        handler (after all scripts are loaded).

        you can always make content hidden, and show it after parsing it.

        Show
        Davor Hrg added a comment - this is simply untrue. QUOTE: "Otherwise it is impossible to execute Javascript before the page renders..." A listener to onload can be added no matter where script tags are. onload will not be called until all page resources are loaded. if you use tapestry to add script to the page, the script will be called inside an onload handler (after all scripts are loaded). you can always make content hidden, and show it after parsing it.
        Hide
        Onno Scheffers added a comment -

        > But the DOM isn't loaded any faster just because you put the script tag further up in your document, you still need to load the entire page first.

        Well, the DOM is loaded faster if you put the scripts tags outside of the HEAD tag, since the browsers waits until everything in the HEAD is loaded, while it doesn't wait for resources outside of the head. So it does give a speedup if you place the scripts at the bottom of the file.

        > If I understand your concern, you want your user to see a blank page while waiting for scripts in <head> to load, rather than your user seeing the full page while waiting for scripts at the bottom of the page to load?

        That's right. Usually css-files are placed in the HEAD to make sure they are loaded before rendering. But often Javascript is used for toggling styles or hiding things as well, so it should be possible to execute that Javascript before the page is shown to the user.

        > I believe that's the opposite of what most others want so it should probably not be the default behaviour.

        I'm not sure I agree. If you look at any of the example pages of any of the Javascript-frameworks. They pretty much all use a dom:loaded event to alter the HTML before the page gets rendered and it's not just Prototype. It's also one of the first items the JQuery tutorial teaches you for example:
        http://docs.jquery.com/Tutorials:Getting_Started_with_jQuery#Hello_jQuery

        > I believe putting scripts at the bottom so that the page renders faster is likely what most people want. If someone actually wants the page to render slower or needs to execute a script before the dom is loaded, there could be a simple workaround or a configuration option for putting scripts at the top of the page.

        It is really quite common to have the Javascript in the HEAD section and currently it is not possible in Tapestry. It should at least be configurable. I still think it should be default behavior as well since you are forcing people to change the configuration to do the most-common thing, while usually you'll start configuring things only if you want to tweak the performance.

        Also, the overhead of placing those files in the HEAD is minimal, since the browser caches the js-files.

        Show
        Onno Scheffers added a comment - > But the DOM isn't loaded any faster just because you put the script tag further up in your document, you still need to load the entire page first. Well, the DOM is loaded faster if you put the scripts tags outside of the HEAD tag, since the browsers waits until everything in the HEAD is loaded, while it doesn't wait for resources outside of the head. So it does give a speedup if you place the scripts at the bottom of the file. > If I understand your concern, you want your user to see a blank page while waiting for scripts in <head> to load, rather than your user seeing the full page while waiting for scripts at the bottom of the page to load? That's right. Usually css-files are placed in the HEAD to make sure they are loaded before rendering. But often Javascript is used for toggling styles or hiding things as well, so it should be possible to execute that Javascript before the page is shown to the user. > I believe that's the opposite of what most others want so it should probably not be the default behaviour. I'm not sure I agree. If you look at any of the example pages of any of the Javascript-frameworks. They pretty much all use a dom:loaded event to alter the HTML before the page gets rendered and it's not just Prototype. It's also one of the first items the JQuery tutorial teaches you for example: http://docs.jquery.com/Tutorials:Getting_Started_with_jQuery#Hello_jQuery > I believe putting scripts at the bottom so that the page renders faster is likely what most people want. If someone actually wants the page to render slower or needs to execute a script before the dom is loaded, there could be a simple workaround or a configuration option for putting scripts at the top of the page. It is really quite common to have the Javascript in the HEAD section and currently it is not possible in Tapestry. It should at least be configurable. I still think it should be default behavior as well since you are forcing people to change the configuration to do the most-common thing, while usually you'll start configuring things only if you want to tweak the performance. Also, the overhead of placing those files in the HEAD is minimal, since the browser caches the js-files.
        Hide
        Onno Scheffers added a comment -

        > this is simply untrue.
        > QUOTE: "Otherwise it is impossible to execute Javascript before the page renders..."

        You cut off the part of the sentence that really mattered.

        An onload-event is triggered when ALL resources are loaded (including images and script-files). The page starts rendering as soon as possible (even before the resources are loaded). Have you never seen the text popup in a browser before the images were loaded? That's the difference between dom:loaded and the onload event.

        This means rendering starts BEFORE the Prototype libraries are even loaded, which means the custom Javascript will be executed even later, since that relies on the Prototype libraries being loaded. It really is true. I can even prove it with example-code if you don't believe me.

        > A listener to onload can be added no matter where script tags are. onload will not be called until all page resources are loaded.

        Indeed, but the page will be rendered before the resources are loaded.. you can see the text appear in the browser before the images are loaded quite clearly.

        > you can always make content hidden, and show it after parsing it.

        But not before rendering it!

        Show
        Onno Scheffers added a comment - > this is simply untrue. > QUOTE: "Otherwise it is impossible to execute Javascript before the page renders..." You cut off the part of the sentence that really mattered. An onload-event is triggered when ALL resources are loaded (including images and script-files). The page starts rendering as soon as possible (even before the resources are loaded). Have you never seen the text popup in a browser before the images were loaded? That's the difference between dom:loaded and the onload event. This means rendering starts BEFORE the Prototype libraries are even loaded, which means the custom Javascript will be executed even later, since that relies on the Prototype libraries being loaded. It really is true. I can even prove it with example-code if you don't believe me. > A listener to onload can be added no matter where script tags are. onload will not be called until all page resources are loaded. Indeed, but the page will be rendered before the resources are loaded.. you can see the text appear in the browser before the images are loaded quite clearly. > you can always make content hidden, and show it after parsing it. But not before rendering it!
        Hide
        Martin Strand added a comment -

        I understand your concern Onno, I just believe the more common case is to have it the other way around - wanting the page to render as quickly as possible on the first visit.

        You say the overhead is minimal since the script is cached, but that also applies to your own use case.
        Your problem occurs when the page is fully rendered and the client waits for those scripts at the bottom to load before you get to manipulate the DOM. That would not be an issue once the scripts are cached.

        Show
        Martin Strand added a comment - I understand your concern Onno, I just believe the more common case is to have it the other way around - wanting the page to render as quickly as possible on the first visit. You say the overhead is minimal since the script is cached, but that also applies to your own use case. Your problem occurs when the page is fully rendered and the client waits for those scripts at the bottom to load before you get to manipulate the DOM. That would not be an issue once the scripts are cached.
        Hide
        Onno Scheffers added a comment -

        > You say the overhead is minimal since the script is cached, but that also applies to your own use case.

        Actually, it doesn't.
        The browser only loads two resources at once. This means the js-files, which are the last resources on the page, will not be resolved until all other resources are loaded (e.g. all images and all other scripts). So even if they are cached, the slowdown is considerable because I have to wait for all other resources to be loaded before the script gets executed.

        Meanwhile the page has been rendered and is visible to the user. The user may even start clicking on items that shouldn't even have been visible to him or her, depending on how big the images on the page are or how slow the connection is.

        Show
        Onno Scheffers added a comment - > You say the overhead is minimal since the script is cached, but that also applies to your own use case. Actually, it doesn't. The browser only loads two resources at once. This means the js-files, which are the last resources on the page, will not be resolved until all other resources are loaded (e.g. all images and all other scripts). So even if they are cached, the slowdown is considerable because I have to wait for all other resources to be loaded before the script gets executed. Meanwhile the page has been rendered and is visible to the user. The user may even start clicking on items that shouldn't even have been visible to him or her, depending on how big the images on the page are or how slow the connection is.
        Hide
        Martin Strand added a comment -

        > The browser only loads two resources at once.
        I honestly had no idea that the limit would affect cached resources too, but if that really is how it works you certainly bring up a valid point for cases where the DOM must be manipulated with javascript before being rendered.

        Show
        Martin Strand added a comment - > The browser only loads two resources at once. I honestly had no idea that the limit would affect cached resources too, but if that really is how it works you certainly bring up a valid point for cases where the DOM must be manipulated with javascript before being rendered.
        Hide
        Filip S. Adamsen added a comment -

        Actually, dom:loaded will be called before images etc. are loaded, so it's rarely a problem.

        Show
        Filip S. Adamsen added a comment - Actually, dom:loaded will be called before images etc. are loaded, so it's rarely a problem.
        Hide
        Onno Scheffers added a comment -

        To be honest, I didn't know it worked like this either and it seems to differ per browser. On Firefox 3 (OSX) the effect is pretty obvious, even if you include just one js-file at the end of the file. On other browsers the problems start to show up only when more js-files are added to the end of the file (like Tapestry does).

        So some browsers are smarter and do immediately include cached resources, but even they have to include the javascript files one after another, which still takes many seconds with big libraries like Prototype and scipt.aculo.us.

        Here's a test-case, using big images and external resources to make things more apparent, tested in Firefox 3, Opera and Safari (OSX, Mac Pro 8-core). Opera is the fastest, Firefox seems to be slowest.

        • sayHello.js is a Javacript file that simply shows an alert-box when it gets executed. It will be included in the two example pages.
        • TestSlow.html is how Tapestry does it now. I added a couple of large images on the page and included several external js-files to the end of the page. It is most visible on first page-load (or after the cache is cleared). The Javascript isn't executed until most of the resources are loaded or have started loading.
        • TestFast.html includes a couple of js-files in the HEAD section. The page stays white a little longer, but the alert-box is shown immediately and before the page renders.
        Show
        Onno Scheffers added a comment - To be honest, I didn't know it worked like this either and it seems to differ per browser. On Firefox 3 (OSX) the effect is pretty obvious, even if you include just one js-file at the end of the file. On other browsers the problems start to show up only when more js-files are added to the end of the file (like Tapestry does). So some browsers are smarter and do immediately include cached resources, but even they have to include the javascript files one after another, which still takes many seconds with big libraries like Prototype and scipt.aculo.us. Here's a test-case, using big images and external resources to make things more apparent, tested in Firefox 3, Opera and Safari (OSX, Mac Pro 8-core). Opera is the fastest, Firefox seems to be slowest. sayHello.js is a Javacript file that simply shows an alert-box when it gets executed. It will be included in the two example pages. TestSlow.html is how Tapestry does it now. I added a couple of large images on the page and included several external js-files to the end of the page. It is most visible on first page-load (or after the cache is cleared). The Javascript isn't executed until most of the resources are loaded or have started loading. TestFast.html includes a couple of js-files in the HEAD section. The page stays white a little longer, but the alert-box is shown immediately and before the page renders.
        Hide
        Onno Scheffers added a comment -

        @Filip

        Hi Filip,

        I know that dom:loaded will be called before images etc. are loaded, but that is just my point: The js-files that attach to the event may not be loaded before the images. As a matter of fact: The dom:loaded event should have passed by the time the scripts are loaded (since the browser doesn't wait for them, but the dom is ready).

        Prototype seems to be smart and fires the event, even if it the listener is attached after the event actually occured, but I've had this problem with JQuery a while back where Javascripts couldn't act on the domloaded event anymore because they were loaded after the dom was ready.

        Show
        Onno Scheffers added a comment - @Filip Hi Filip, I know that dom:loaded will be called before images etc. are loaded, but that is just my point: The js-files that attach to the event may not be loaded before the images. As a matter of fact: The dom:loaded event should have passed by the time the scripts are loaded (since the browser doesn't wait for them, but the dom is ready). Prototype seems to be smart and fires the event, even if it the listener is attached after the event actually occured, but I've had this problem with JQuery a while back where Javascripts couldn't act on the domloaded event anymore because they were loaded after the dom was ready.
        Hide
        Davor Hrg added a comment -

        ok,
        disclaimer: I'm talking from my experience, I may not be right after all

        I understand you wanting to do things before onload,
        and right after dom is loaded.

        This however does not mean that scripts can not be on the bottom.

        If you want to change the whole document before rendering
        you definitely must wait for the whole html to be read by browser,
        and if the page is big enough (a huge table 50x100 with loads of markup)
        the rendering will start even before the whole document is read.

        if you have an example of the page where it is a clear difference
        when changing content before render and simply onload
        I'm convinced I can make it work with scripts on the bottom.

        .....
        you can definitely hide the content before rendering and without a line of js
        the example is untested, but you can understand what I mean....

        <div id="hideDiv" style="display:none">
        ...content.....
        </div>
        <script .... js libs...>
        <script>
        //transform dom (all dom is loaded if the script is at bottom)

        var div = document.getElementById("hideDiv");

        //show div
        //div.style.display = "block";

        //or remove div from body and move all it's children to body
        console.log(div.firstChild);
        while(div.firstChild)

        { document.body.appendChild(div.firstChild);//it will be removed from div automatically }

        document.body.removeChild(div);

        </script>

        adding scripts to bottom or top definitely makes a difference,
        having it as an option brings more complexity to users that are
        more server side centric.

        If you want to manipulate dom you can do it on the server side as well
        (not as many options there though)

        here is the TestSlow that does not show content before the alert
        (the alert will be shown later because the images will start loading before
        since they are before scripts in the source)

        Show
        Davor Hrg added a comment - ok, disclaimer: I'm talking from my experience, I may not be right after all I understand you wanting to do things before onload, and right after dom is loaded. This however does not mean that scripts can not be on the bottom. If you want to change the whole document before rendering you definitely must wait for the whole html to be read by browser, and if the page is big enough (a huge table 50x100 with loads of markup) the rendering will start even before the whole document is read. if you have an example of the page where it is a clear difference when changing content before render and simply onload I'm convinced I can make it work with scripts on the bottom. ..... you can definitely hide the content before rendering and without a line of js the example is untested, but you can understand what I mean.... <div id="hideDiv" style="display:none"> ...content..... </div> <script .... js libs...> <script> //transform dom (all dom is loaded if the script is at bottom) var div = document.getElementById("hideDiv"); //show div //div.style.display = "block"; //or remove div from body and move all it's children to body console.log(div.firstChild); while(div.firstChild) { document.body.appendChild(div.firstChild);//it will be removed from div automatically } document.body.removeChild(div); </script> adding scripts to bottom or top definitely makes a difference, having it as an option brings more complexity to users that are more server side centric. If you want to manipulate dom you can do it on the server side as well (not as many options there though) here is the TestSlow that does not show content before the alert (the alert will be shown later because the images will start loading before since they are before scripts in the source)
        Hide
        Onno Scheffers added a comment - - edited

        @Davor Hrg

        Your solution wouldn't work because if you generate invisible content on the server and then make it visible on the client using Javascript, then the application won't work for people that have Javascript disabled. For me it is an absolute requirement to have pages work without Javascript.

        In my case I'm showing a couple of questions on the page. For each question it is possible to add a note and to upload a file. Since adding a note or attaching a file is the exception rather than the rule and since the textarea and upload-field take up precious space and may even confuse users, we hide those areas using Javascript and add a coule of icons for expanding the areas when you need them.
        This improves the user-experience for people that have Javascript enabled, but still allows people without Javascript to see the areas and use their functionality.

        This is really a very common approach that is being used in several companies I've worked for, especially for government work where it is essential users can access the web applications without Javascript.

        Show
        Onno Scheffers added a comment - - edited @Davor Hrg Your solution wouldn't work because if you generate invisible content on the server and then make it visible on the client using Javascript, then the application won't work for people that have Javascript disabled. For me it is an absolute requirement to have pages work without Javascript. In my case I'm showing a couple of questions on the page. For each question it is possible to add a note and to upload a file. Since adding a note or attaching a file is the exception rather than the rule and since the textarea and upload-field take up precious space and may even confuse users, we hide those areas using Javascript and add a coule of icons for expanding the areas when you need them. This improves the user-experience for people that have Javascript enabled, but still allows people without Javascript to see the areas and use their functionality. This is really a very common approach that is being used in several companies I've worked for, especially for government work where it is essential users can access the web applications without Javascript.
        Hide
        Davor Hrg added a comment -

        ok,

        I agree that this should be considered as optional,
        but the default should be scripts at bottom.

        and another solution with considering the users without js

        add this script as first line after body:
        <script>document.body.style.visibility = "hidden";</script>

        and this script at the bottom (after doing your DOM magic)
        <script>document.body.style.visibility = "visible";</script>

        this will have no effect for users with disabled js
        it will make content hidden until it is modified by the script
        it will work no matter if scripts are in HEAD or at bottom
        this approach can even be used in combination with classic onload
        (before this last snippet of course)

        Show
        Davor Hrg added a comment - ok, I agree that this should be considered as optional, but the default should be scripts at bottom. and another solution with considering the users without js add this script as first line after body: <script>document.body.style.visibility = "hidden";</script> and this script at the bottom (after doing your DOM magic) <script>document.body.style.visibility = "visible";</script> this will have no effect for users with disabled js it will make content hidden until it is modified by the script it will work no matter if scripts are in HEAD or at bottom this approach can even be used in combination with classic onload (before this last snippet of course)
        Hide
        Onno Scheffers added a comment -

        Hi Davor,

        I'm glad you agree that we should be able to at least configure where the scripts are inserted.

        The problem with the solution you give here is that
        1) It is a hack to get something working that is actually quite common behavior.
        2) It makes the page visible only after all the resources are loaded or loading and the scripts are all done running. This slows down page-loading instead of making it faster, defeating the whole purpose of adding the scripts to the end of the page.

        I think we all agree now that it does make a difference if the scripts are included in the head-section or at the bottom of the page and it may cause problems for people.

        If you look around at existing websites, nearly all of them have the scripts included in the head section. It is the most common approach and therefore the expected behavior for most people using the framework. Therefore I think the default behavior should be to include those file in the head-section.

        But if it is at least configurable, I'm happy.

        Show
        Onno Scheffers added a comment - Hi Davor, I'm glad you agree that we should be able to at least configure where the scripts are inserted. The problem with the solution you give here is that 1) It is a hack to get something working that is actually quite common behavior. 2) It makes the page visible only after all the resources are loaded or loading and the scripts are all done running. This slows down page-loading instead of making it faster, defeating the whole purpose of adding the scripts to the end of the page. I think we all agree now that it does make a difference if the scripts are included in the head-section or at the bottom of the page and it may cause problems for people. If you look around at existing websites, nearly all of them have the scripts included in the head section. It is the most common approach and therefore the expected behavior for most people using the framework. Therefore I think the default behavior should be to include those file in the head-section. But if it is at least configurable, I'm happy.

          People

          • Assignee:
            Howard M. Lewis Ship
            Reporter:
            Howard M. Lewis Ship
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development