4

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.

6
  • Ii really do not understand your code as you have posted it. How is content.html related with base.html? If it provides {% block main-content %} then clearly the script is called before jQuery is loaded. Commented Jul 3, 2015 at 9:51
  • Yes of course it is called before (I updated the code with a more obvious case of this) The whole point is we need to load the content before calling the JS (to not slower the page) but we have to wait all JS are loaded before calling the script in content.html. Commented Jul 3, 2015 at 9:54
  • Or maybe it's ok if jquery is the ONLY script loaded before content? Commented Jul 3, 2015 at 9:55
  • You cannot clearly expect a jQuery script to be executed before jQuery is loaded. You have to change your script order. Updated answer. Commented Jul 3, 2015 at 9:56
  • Of course if I load jquery, then my content, then my script, this works well. But the question is: is that acceptable to load a BIG Javascript file BEFORE loading my content? I guess it's not a good practice, and I hoped there would be a better solution, I suppose I was wrong :) Commented Jul 3, 2015 at 10:00

2 Answers 2

12

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Ditto. This was exactly what I was looking for.
5

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:

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)

4 Comments

Thanks. I do use jQuery but it's not loaded yet so '$' is not defined when calling $(document)
Welcome, you simply have to include the jquery script before your script.
It's already the case. In base.html, Jquery is loaded, then my script is loaded. But when entering into my main content, no JS is loaded at all (which is normal)
What do you mean "entering main content"? Post the lines where you include jquery please.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.