Passing data from
Twig to JavaScript
Using a data attribute to pass a single field
If you have a small amount of data to share with JavaScript then use a named data attribute.
First add the named data attribute to an element.
I often need it within a loop like this:
{# Add a custom named data attribute containing your data #}
{% for entry in entries %}
<a href="{{ entry.link }}" data-entry-id="{{ entry.id }}">
{{ entry.title }}
</a>
{% endif %}
❗ If the value has a chance of containing a "
or <
then add an escape filter: ...|e('html_attr')
.
Then in JavaScript, select the elements by their data attribute and extract the value from their dataset to get an array of values:
/**
* Grab data attributes with vanilla JavaScript (ES6)
*/
document.addEventListener('DOMContentLoaded', () => {
// Select elements by their data attribute
const entryElements =
document.querySelectorAll('[data-entry-id]');
// Map over each element and extract the data value
const entryIds =
Array.from(entryElements).map(
item => item.dataset.entryId
);
// You'll now have an array containing string values
console.log(entryIds); // eg: ["1", "2", "3"]
});
/**
* Grab data attributes with JQuery
*/
$(() => {
// Select elements by their data attribute
const $entryElements = $('[data-entry-id]');
// Map over each element and extract the data value
const $entryIds =
$.map($entryElements, item => $(item).data('entryId'));
// You'll now have array containing string values
console.log($entryIds); // eg: ["1", "2", "3"]
});
❗ Any data attributes with hypens added after the data-
prefix convert to camelCase within their dataset. eg: data-entry-id=...
converts to entryId
For more information on data attributes take a look at the MDN docs guide on using data attributes.
Using a data attribute to pass multiple fields
The keep our code clean, we’ll use a slightly different tactic to send multiple pieces of data to JavaScript.
The trick is to create an object in Twig, convert the data to JSON then select it with JavaScript:
{# Create an object containing your data #}
{% set entryInfo = {
id: entry.id,
title: entry.title,
subheading: entry.showSub ? entry.subheading : null,
} %}
{# Convert the object to JSON, replace the single quotes
and convert to raw.
Be sure to use single quotes around the raw content. #}
<div data-entry-info='{{ entryInfo|json_encode|replace("'", "'")|raw }}'>
...
</div>
Then in JavaScript, select the elements by their data attribute and JSON.parse
each dataset value to get an array of objects:
/**
* Grab data attribute objects with vanilla JavaScript (ES6)
*/
document.addEventListener('DOMContentLoaded', () => {
// Select elements by their data attribute
const entryInfoElements =
document.querySelectorAll('[data-entry-info]');
// Map over each element and extract the data value
const entryInfoObjects =
Array.from(entryInfoElements).map(
item => JSON.parse(item.dataset.entryInfo)
);
// You'll now have an array of objects to work with
console.log(entryInfoObjects);
// eg: [{id: 1, subheading: "...", title: "..."}]
});
/**
* Grab data attribute objects with JQuery
*/
$(() => {
// Select elements by their data attribute
const $entryInfoElements = $('[data-entry-info]');
// Map over each element and extract the data value
const $entryInfoObjects =
$.map($entryInfoElements, item => $(item).data('entryInfo'));
// You'll now have array of objects to work with
console.log($entryInfoObjects); // eg: [{id: 1, subheading: "...", title: "..."}]
});
Why we use the raw filter
When we convert the object above to JSON it looks like this:
<!-- After using |json_encode -->
{"id":1,"title":"...","subheading":"..."}
The twig documentation for escape shows we can use |e('html_attr')
to escape instead of |raw
. It allows us to use double quotes around the value in our Twig but it creates a large amount of unneeded code:
<!-- After using |json_encode|e('html_attr') -->
{"id":1,"title":"...","subheading":"..."}
The raw
filter prunes the JSON object down but now requires single quotes to attach it to the data attribute because it uses double quotes within the object:
<!-- After using |json_encode|replace("'", "'")|raw -->
{"id":1,"title":"...","subheading":"..."}
<!-- This is why the data attribute is single quoted: -->
<div
data-entry-info='{"id":1,"title":"...","subheading":"..."}'
>...</div>
Add the data within a script tag
If you have general data that’s not directly tied to an item then creating a JavaScript variable within a script tag can be a good option:
<script>
// Create an object for your data
var entryInfo = {
id: '{{ entry.id }}',
title: '{{ entry.title|e('js') }}',
subheading: '{{ entry.showSub ? entry.subheading|e('js') : null }}',
};
// Or create a single variable
var entryTitle = '{{ entry.title|e('js') }}'
</script>
Then simply grab the variable from the browsers window object within JavaScript:
document.addEventListener('DOMContentLoaded', () => {
// Get the object from the window object
const entryInfo = window.entryInfo;
// Now you have a JavaScript object
console.log(entryInfo);
});
TL;DR
For data that’s related to an element
- If you have a single field of data
Use a named data attribute, eg:data-the-name="..."
. - If you have multiple fields of data
Use a JSON encoded object and attach it to a data attribute.
For generalised page data
Create a JSON encoded object or variable and place it within a script tag.
Further reading
- For more information on data attributes take a look at the MDN docs guide on using data attributes.
- Check Twigs official documentation on passing variables from twig to js.