django-js-asset
script tag with additional attributes for django.forms.Media
Rank: #2751Downloads: 2,030,917 (30 days)Stars: 24Forks: 11
Description
==================================================================
django-js-asset -- JS, CSS and JSON support for django.forms.Media
==================================================================
.. image:: https://github.com/matthiask/django-js-asset/workflows/Tests/badge.svg
:target: https://github.com/matthiask/django-js-asset
**Note!** `Django 5.2 adds its own support for JavaScript objects
<https://docs.djangoproject.com/en/dev/topics/forms/media/#script-objects>`__.
This library has a slightly different API and also supports much older versions
of Django, *and* it also supports CSS and JSON tags.
Usage
=====
Use this to insert a script tag via ``forms.Media`` containing additional
attributes (such as ``id`` and ``data-*`` for CSP-compatible data
injection.):
.. code-block:: python
from js_asset import JS
forms.Media(js=[
JS("asset.js", {
"id": "asset-script",
"data-answer": "42",
}),
])
The rendered media tag (via ``{{ media.js }}`` or ``{{ media }}`` will
now contain a script tag as follows, without line breaks:
.. code-block:: html
<script type="text/javascript" src="/static/asset.js"
data-answer="42" id="asset-script"></script>
The attributes are automatically escaped. The data attributes may now be
accessed inside ``asset.js``:
.. code-block:: javascript
var answer = document.querySelector("#asset-script").dataset.answer;
Also, because the implementation of ``static`` differs between supported
Django versions (older do not take the presence of
``django.contrib.staticfiles`` in ``INSTALLED_APPS`` into account), a
``js_asset.static`` function is provided which does the right thing
automatically.
CSS and JSON support
====================
Since 3.0 django-js-asset also ships a ``CSS`` and ``JSON`` media object which
can be used to ship stylesheets, inline styles and JSON blobs to the frontend.
It's recommended to pass those through ``forms.Media(js=[])`` as well since
``js`` is a simple list while ``css`` uses a dictionary keyed with the media to
use for the stylesheet.
So, you can add everything at once:
.. code-block:: python
from js_asset import CSS, JS, JSON
forms.Media(js=[
JSON({"configuration": 42}, id="widget-configuration"),
CSS("widget/style.css"),
CSS("p{color:red;}", inline=True),
JS("widget/script.js", {"type": "module"}),
])
This produces:
.. code-block:: html
<script id="widget-configuration" type="application/json">{"configuration": 42}</script>
<link href="/static/widget/style.css" media="all" rel="stylesheet">
<style media="all">p{color:red;}</style>
<script src="/static/widget/script.js" type="module"></script>
Compatibility
=============
At the time of writing this app is compatible with Django 4.2 and better
(up to and including the Django main branch), but have a look at the
`tox configuration
<https://github.com/matthiask/django-js-asset/blob/main/tox.ini>`_ for
definitive answers.
Extremely experimental importmap support
========================================
django-js-asset ships an extremely experimental implementation adding support
for using `importmaps
<https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap>`_.
One of the reasons why importmaps are useful when used with Django is that this
easily allows us to use the file name mangling offered for example by Django
``ManifestStaticFilesStorage`` without having to rewrite import statements in
scripts themselves.
Browser support for multiple importmaps is not generally available; at the time
of writing (February 2025) it's not even clear if Mozilla wants to support them
ever, so merging importmaps is -- for now -- the only viable way to use them in
production. Because of this the implementation uses a global importmap variable
where new entries can be added to and a context processor to make the importmap
available to templates.
The ``importmap`` object can be imported from ``js_asset``. Usage is as follows:
.. code-block:: python
# static is an alias for Django's static() function used in the
# {% static %} template tag.
from js_asset import JS, static, importmap
# Run this during project initialization, e.g. in App.ready or whereever.
importmap.update({
"imports": {
"my-library": static("my-library.js"),
},
})
You have to add ``js_asset.context_processors.importmap`` to the list of
context processors in your settings (or choose some other way of making the
``importmap`` object available in templates) and add ``{{ importmap }}``
somewhere in your base template, preferrably at the top before including any
scripts.
When you've done that you can start profiting from the importmap by adding
JavaScript modules:
.. code-block:: python
# Example for adding a code.js JavaScript *module*
forms.Media(js=[
JS("code.js", {"type": "module"}),
])
The code in ``code.js`` can now use a JavaScript import to import assets from
the library, even though the library's filename may contain hashes not known at
programming time:
.. code-block:: javascript
import { Stuff } from "my-library"