Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django/jQuery: handling template inheritence and JS files loading

In Django, how can you handle the fact that you need to wait for that a JS file is loaded before actually using it?

let's see the problem with this example:

base.html

<!DOCTYPE html>
<html>

<head>...</head>
<body>
    {% include "content.html" %}

    <script src="jquery.js"></script>
    <script src="awesome-script.js"></script>
</body>
</html>

content.html

<script>
    $(document).ready(function(){
        ...
    });
</script>

This logically fail ($ is undefined). I could load jQuery before calling the script, but I'm trying to avoid loading JS file before my main content to keep the website loading as fast as possible.

So, what can I do? Thanks.

like image 671
David D. Avatar asked Dec 03 '22 16:12

David D.


2 Answers

Extending Wtower's suggestion - keep his accepted.

I would really insist on using the template inheritance based approach in his examples. I would like to introduce a few more elements to that approach, to cover some other common needs :

<!DOCTYPE html>
<html>
<head>{% block scripts-head %}{% endblock %}</head>
<body>
    {% block content %}{% endblock %}
    {% block scripts %}
        <script src="jquery.js"></script>
    {% endblock %}
    <script>{% block script-inline %}{% endblock %}</script>
</body>
</html>

There are 3 ideas here:

  • Adding a placeholder in the header, in case you could need scripts there at some point. Self explanatory.
  • Including common scripts in the base file. If they are common, the belong in the base file, you should not have to repeat yourself in every template. Yet you put it inside the block, so it can be overriden along the hierarchy.

    {% extends "base.html" %}
    {% block scripts %}
        {{ block.super }}
        <script src="a-local-lib.js"></script>
    {% endblock %}
    

    The key is in using {{ block.super }} to bring any script that was defined in the parent template. It works especially well when you have several levels of inheritance in your templates. You get to control whether script go before or after inherited scripts. And of course, you can completely override the block, not including {{ block.super }} if you so wish.

  • Basically the same idea, but with raw javascript. You use it the same way: every template that needs to include some inline javascript will have its {{ block script-inline }}, and will start with {{ block.super }} so whatever the parent put in there is still included.

    For instance, I use Ember in my project, and have a couple of initializers to setup project settings and load bootstrap data. My base app-loading templates has a global project settings initializer, and child templates define local settings and data.

like image 181
spectras Avatar answered Dec 05 '22 07:12

spectras


Since your script uses jQuery, you can simply use the $(document).ready() and $(window).load() functions of jQuery to bind a function on the event that DOM is ready and all window contents have been loaded, respectively.

If you do not use jQuery, take a look at these relative questions to understand how to imitate the above behaviour with pure JS:

  • pure JavaScript equivalent to jQuery's $.ready() how to call a function when the page/dom is ready for it
  • Javascript - How to detect if document has loaded

EDIT 1: The inclusion order matters. You have to include the jQuery scripts before any scripts that require jQuery are executed.

EDIT 2: You can organize your templates better by keeping the scripts separately from the main content, either with a second template:

base.html

<!DOCTYPE html>
<html>

<head>...</head>
<body>
    {% include "content.html" %}
    {% include "js.html" %}
</body>
</html>

js.html

<script src="jquery.js"></script>
<script src="awesome-script.js"></script>
<script>
    $(document).ready(function(){
        ...
    });
</script>

(in this case you render base.html)

Or with blocks (recommended):

base.html

<!DOCTYPE html>
<html>

<head>...</head>
<body>
    {% block content %}{% endblock %}
    {% block scripts %}{% endblock %}
</body>
</html>

content.html

{% extends 'base.html' %}
{% block content %}
    ...
{% endblock %}
{% block scripts %}
    <script src="jquery.js"></script>
    <script src="awesome-script.js"></script>
    <script>
        $(document).ready(function(){
            ...
        });
    </script>
{% endblock %}    

(in this case you render content.html)

like image 41
Wtower Avatar answered Dec 05 '22 05:12

Wtower