How to pass a list from Python, by Jinja2 to JavaScript
Let's say I have a Python variable:
list_of_items = ['1','2','3','4','5']
and I pass it to Jinja by rendering HTML, and I also have a function in JavaScript called somefunction(variable)
. I am trying to pass each item of list_of_items
. I tried something like this:
{% for item in list_of_items %}
<span onclick="somefunction({{item}})">{{item}}</span><br>
{% endfor %}
Is it possible to pass a list from Python to JavaScript or should I pass each item from list one by one in a loop? How can I do this?
To pass some context data to javascript code, you have to serialize it in a way it will be "understood" by javascript (namely JSON). You also need to mark it as safe using the safe
Jinja filter, to prevent your data from being htmlescaped.
You can achieve this by doing something like that:
The view
import json
@app.route('/')
def my_view():
data = [1, 'foo']
return render_template('index.html', data=json.dumps(data))
The template
<script type="text/javascript">
function test_func(data) {
console.log(data);
}
test_func({{ data|safe }})
</script>
Edit - exact answer
So, to achieve exactly what you want (loop over a list of items, and pass them to a javascript function), you'd need to serialize every item in your list separately. Your code would then look like this:
The view
import json
@app.route('/')
def my_view():
data = [1, "foo"]
return render_template('index.html', data=map(json.dumps, data))
The template
{% for item in data %}
<span onclick=someFunction({{ item|safe }});>{{ item }}</span>
{% endfor %}
Edit 2
In my example, I use Flask
, I don't know what framework you're using, but you got the idea, you just have to make it fit the framework you use.
Edit 3 (Security warning)
NEVER EVER DO THIS WITH USER-SUPPLIED DATA, ONLY DO THIS WITH TRUSTED DATA!
Otherwise, you would expose your application to XSS vulnerabilities!
I had a similar problem using Flask, but I did not have to resort to JSON. I just passed a list letters = ['a','b','c']
with render_template('show_entries.html', letters=letters)
, and set
var letters = {{ letters|safe }}
in my javascript code. Jinja2 replaced {{ letters }}
with ['a','b','c']
, which javascript interpreted as an array of strings.
You can do this with Jinja's tojson
filter, which
Dumps a structure to JSON so that it’s safe to use in
<script>
tags [and] in any place in HTML with the notable exception of double quoted attributes.
For example, in your Python, write:
some_template.render(list_of_items=list_of_items)
... or, in the context of a Flask endpoint:
return render_template('your_template.html', list_of_items=list_of_items)
Then in your template, write this:
{% for item in list_of_items %}
<span onclick='somefunction({{item | tojson}})'>{{item}}</span><br>
{% endfor %}
(Note that the onclick
attribute is single-quoted. This is necessary since |tojson
escapes '
characters but not "
characters in its output, meaning that it can be safely used in single-quoted HTML attributes but not double-quoted ones.)
Or, to use list_of_items
in an inline script instead of an HTML attribute, write this:
<script>
const jsArrayOfItems = {{list_of_items | tojson}};
// ... do something with jsArrayOfItems in JavaScript ...
</script>
DON'T use json.dumps
to JSON-encode variables in your Python code and pass the resulting JSON text to your template. This will produce incorrect output for some string values, and will expose you to XSS if you're trying to encode user-provided values. This is because Python's built-in json.dumps
doesn't escape characters like <
and >
(which need escaping to safely template values into inline <script>
s, as noted at https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements) or single quotes (which need escaping to safely template values into single-quoted HTML attributes).
If you're using Flask, note that Flask injects a custom tojson
filter instead of using Jinja's version. However, everything written above still applies. The two versions behave almost identically; Flask's just allows for some app-specific configuration that isn't available in Jinja's version.
To add up on the selected answer, I have been testing a new option that is working too using jinja2 and flask:
@app.route('/')
def my_view():
data = [1, 2, 3, 4, 5]
return render_template('index.html', data=data)
The template:
<script>
console.log( {{ data | tojson }} )
</script>
the output of the rendered template:
<script>
console.log( [1, 2, 3, 4] )
</script>
The safe
could be added but as well like {{ data | tojson | safe }}
to avoid html escape but it is working without too.