views:

137

answers:

2

I'm curious about people's opinion's and thoughts about this situation. The reason I'd like to lazy load javascript is because of performance. Loading javascript at the end of the body reduces the browser blocking and ends up with much faster page loads.

But there is some automation I'm using to generate the html (django specifically). This automation has the convenience of allowing forms to be built with "Widgets" that output content it needs to render the entire widget (extra javascript, css, ...). The problem is that the widget wants to output javascript immediately into the middle of the document, but I want to ensure all javascript loads at the end of the body.

When the following widget is added to a form, you can see it renders some <script>...</script> tags:

class AutoCompleteTagInput(forms.TextInput):
    class Media:                                                    
        css = {
            'all': ('css/jquery.autocomplete.css', )
        }                                             
        js = (
            'js/jquery.bgiframe.js',
            'js/jquery.ajaxQueue.js',                               
            'js/jquery.autocomplete.js',
        )       

    def render(self, name, value, attrs=None):        
        output = super(AutoCompleteTagInput, self).render(name, value, attrs)
        page_tags = Tag.objects.usage_for_model(DataSet)
        tag_list = simplejson.dumps([tag.name for tag in page_tags],
                                    ensure_ascii=False)
        return mark_safe(u'''<script type="text/javascript">                  
            jQuery("#id_%s").autocomplete(%s, {
                width: 150,                                         
                max: 10,
                highlight: false,
                scroll: true,
                scrollHeight: 100,
                matchContains: true,
                autoFill: true              
        });                               
        </script>''' % (name, tag_list,)) + output

What I'm proposing is that if someone uses a <div class=".lazy-js">...</div> with some css (.lazy-js { display: none; }) and some javascript (jQuery('.lazy-js').each(function(index) { eval(jQuery(this).text()); }), you can effectively force all javascript to load at the end of page load:

class AutoCompleteTagInput(forms.TextInput):
    class Media:                                                    
        css = {
            'all': ('css/jquery.autocomplete.css', )
        }                                             
        js = (
            'js/jquery.bgiframe.js',
            'js/jquery.ajaxQueue.js',                               
            'js/jquery.autocomplete.js',
        )       

    def render(self, name, value, attrs=None):        
        output = super(AutoCompleteTagInput, self).render(name, value, attrs)
        page_tags = Tag.objects.usage_for_model(DataSet)
        tag_list = simplejson.dumps([tag.name for tag in page_tags],
                                    ensure_ascii=False)
        return mark_safe(u'''<div class="lazy-js">                  
            jQuery("#id_%s").autocomplete(%s, {
                width: 150,                                         
                max: 10,
                highlight: false,
                scroll: true,
                scrollHeight: 100,
                matchContains: true,
                autoFill: true              
        });                               
        </div>''' % (name, tag_list,)) + output

Nevermind all the details of my specific implementation (the specific media involved), I'm looking for a consensus on whether the method of using lazy-loaded javascript through hidden a hidden tags can pose issues whether security or other related?

One of the most convenient parts about this is that it follows the DRY principle rather well IMO because you don't need to hack up a specific lazy-load for each instance in the page. It just "works".

UPDATE: I'm not sure if django has the ability to queue things (via fancy template inheritance or something?) to be output just before the end of the </body>?

A: 

The problem is that the widget wants to output javascript immediately into the middle of the document, but I want to ensure all javascript loads at the end of the body.

Then I would not use that widget. Dynamically supplied client-side script from the client side is a disaster that will break security without you or your user being any wiser. How would you know if that code were compromised, for example?

Are you saying the only hole is when client-side javascript (obviously outside my control) is introduced? I guess, if that's the only hole, then I'm ok with it. Maybe there aren't security issues? I'm just so new to web-programming I want to make sure I'm not opening holes I'm unfamiliar with.
xyld
You might be okay with that only because it is invisible to you. Your clients, however, would not be so okay if they were aware of your sloppy practices allowing hackers to perform identity theft. If this were for a business and you were aware of the risks when writing the code your users would probably sue you to recover any damages suffered. That averaged out to be $11.3 million per incident in 2008 according to Symantec Internet Security Threat Report Volume XIV. What do you want to bet that figure is probably higher now?
+1  A: 

I would prefer a tool that lets me "append" content to the output stream (or) append it to a buffer, that will be outputted at the end of the page, just before the </body> tag.

I'm not sure which commercial tools support this (or does django?) but this is how I've built my frameworks.

As for your question about security/other issues... the script will process whenever it is read (unless you output a script tag with the defer attribute (in IE/newer browsers)) thus unless it is physically moved, it doesn't change the behavior or make it "lazy".

Security wise, pulling content out of the script tag, and calling eval() on it opens you up to the possibility of something executing that you were not planning on. (unlikely, but possible)

scunliffe
So the framework's you're working with allow you to queue things to the end of the body while still writing the main html document?
xyld
Your "Security wise" line is exactly the kind of information I was looking for.
xyld
Correct. I can append to the `body` buffer... or a `footer` buffer... Thus if during "page" processing I want to include a script, I just add it to my "footer"... just before the </body> is rendered, I grab anything in the `footer` buffer and output it.
scunliffe
This would definitely solve my problem also. I guess I'm not sure at this moment if Django has support for this. But this definitely sounds like the "right" way to do it. For now, I guess I'm stuck with lazy-js until I can hack something into Django or discover how other's using django would do this. Thanks.
xyld