admin管理员组

文章数量:1356017

Yahoo's Best Practices for Speeding Up Your Website states:

Put Scripts at the Bottom

That there are two types of scripts in my Django application:

  1. Scripts included in my base (e.g. inherited) template; and
  2. Scripts written inside templates instantiated by templatetags

Scripts to support UI controls are necessarily a part of the template for their supporting template tag to handle stuff like unique IDs and to keep dependent code together.

The problem is that if I follow Yahoo's advice and put the libraries (#1) at the bottom of the page, 100% of the scripts included inline (#2) will fail because the libraries haven't been loaded and parsed yet.

I can't extend a {% block %} element in my base template because anything I do within the context of the templatetag won't propagate outside it -- by design, to avoid variable name conflicts (according to Django's documentation).

Does anyone have any idea how I can defer JavaScript from my templatetags' templates to render at the bottom of my base template?

Yahoo's Best Practices for Speeding Up Your Website states:

Put Scripts at the Bottom

That there are two types of scripts in my Django application:

  1. Scripts included in my base (e.g. inherited) template; and
  2. Scripts written inside templates instantiated by templatetags

Scripts to support UI controls are necessarily a part of the template for their supporting template tag to handle stuff like unique IDs and to keep dependent code together.

The problem is that if I follow Yahoo's advice and put the libraries (#1) at the bottom of the page, 100% of the scripts included inline (#2) will fail because the libraries haven't been loaded and parsed yet.

I can't extend a {% block %} element in my base template because anything I do within the context of the templatetag won't propagate outside it -- by design, to avoid variable name conflicts (according to Django's documentation).

Does anyone have any idea how I can defer JavaScript from my templatetags' templates to render at the bottom of my base template?

Share Improve this question asked Nov 4, 2010 at 21:12 isolationismisolationism 711 silver badge4 bronze badges
Add a ment  | 

4 Answers 4

Reset to default 5

I usually have a base template setup like this.

<html>
<head>

{% block js-head %} Tags that need to go up top  {% endblock js-head %}

</head>
<body>
{% block header %}  Header {% endblock header %}

{% block body %} Body goes here {% endblock body %}

{% block footer %}  Footer {% endblock footer %}

{% block js-foot %}  All other javascript goes here {% endblock js-foot %}
</body>
</html>

Then I can extend this base template and only override the blocks that I care about. I put all javascript that doesn't have to be in the header in js-foot and because it is at the bottom of the template it will load last. If you have have to move where your javascript loads you just need to move the js-foot block around in the base template.

Nothing fancy but it works.

Wrap the scripts that you're including inline in

jQuery(function(){ ... });

Which will execute when the the DOM is ready and scripts have been downloaded.

Another option might be to create some kind of custom template tag like:

{% inlinescript %}
// some javascript code.
{% endinlinescript %}

Which you could use to aggregate inline scripts as execution proceeds. You'd need aggregate this data as your template gets parsed - which gets tricky because template tags have different contexts and this is something you'd want to store in a global context in a custom variable, say inline_scripts.

I'd look at the way Django implements the various with ... as .. constructs in the default template library for an example of how to add your own variable to a context.

Then at the bottom of your page you could do {{ inline_scripts }}.

The easiest solution is the jQuery.ready(function(){}) / jQuery(function(){ }) (The two methods are synonymous).

Or you might want to reconsider Yahoo's advice. There are positive things about inlining your javascript - it can reduce FOUC / FOUBC (Flash of unbehavioured content). Yahoo tends to get kind of pedantic - (just look at the YUI API ;). If you need to rewrite parts of your application for what's going to be moderately perceptible performance improvement, it might not be worth it.


To do the script aggregation (originally based off captureas on django-snippets):

@register.tag(name='aggregate')
def do_aggregate(parser, token):
    try:
        tag_name, args = token.contents.split(None, 1)
    except ValueError:
        raise template.TemplateSyntaxError("'aggregate' node requires a variable name.")
    nodelist = parser.parse(('endaggregate',))
    parser.delete_first_token()
    return AggregateNode(nodelist, args)

class AggregateNode(Node):
    def __init__(self, nodelist, varname):
        self.nodelist = nodelist
        self.varname = varname

    def render(self, context):
        output = self.nodelist.render(context)
        if context.has_key(self.varname):
            context[self.varname] += output
        else:
            context[self.varname] = output
        return ''

Usage:

{% aggregate inline_scripts %}
var foo = 'bar';
{% endaggregate %}

... template code

{% aggregate inline_scripts %}
var baz = 'bar';
{% endaggregate %}

... somewhere near the bottom of your page

{{ inline_scripts }}

There is an application django-sekizai just for that

This worked for me. Wait till the dom loads...

<script>
document.addEventListener("DOMContentLoaded", function (event) {
   {% if my_var %}
      doSomething({{myVar}});
   {% endif %}
});
</script>

本文标签: How to put JavaScript at the bottom of Django pages when using templatetagsStack Overflow