No inline JavaScript
This page is for designers and front-end developers. Templates of Exponential designs carry no script code: every line
of JavaScript is in a file under javascript/, loaded through ezjscore. The page shows the patterns that replace inline
code.
Contents
- What counts as inline
- Handlers: data attributes and delegation
- Values for scripts: JSON blocks
- Progressive enhancement
- Checking
What counts as inline
The same three kinds that the design tests count (AdminContentSecurityPolicyTest, ExtensionInlineScriptsTest):
- a
<script>element with code inside (a<script src="...">, a JSON data block and a<template>are fine); - an event handler attribute:
onclick="",onchange="",onload=""...; - a
javascript:URL.
All three are blocked by a policy with script-src 'self'.
Handlers: data attributes and delegation
Markup says what; the script file says how:
<button type="button" class="js-toggle" data-target="site-nav" aria-expanded="false" aria-controls="site-nav">
'Menu'|i18n( 'design/mytheme' )
</button>
<nav id="site-nav" hidden>...</nav>/* design/mytheme/javascript/mytheme.js */
document.addEventListener('click', function (event) {
var button = event.target.closest('.js-toggle');
if (!button) return;
var target = document.getElementById(button.dataset.target);
var open = button.getAttribute('aria-expanded') === 'true';
button.setAttribute('aria-expanded', String(!open));
target.hidden = open;
});One listener on the document handles every toggle, also ones added later by AJAX. The admin uses the same idea with
data-a4-* actions (the CSP feature page lists them as the replacement for handlers).
Values for scripts: JSON blocks
<script type="application/json" id="mytheme-map">{json_encode( hash(
'lat', $node.data_map.location.content.latitude,
'lng', $node.data_map.location.content.longitude,
'label', $node.name ) )}</script>var data = JSON.parse(document.getElementById('mytheme-map').textContent);json_encode (ezjscore) escapes the values for JSON. A JSON block is data, not code, and is not blocked.
Progressive enhancement
Build pages that work without the script, then add behavior:
- a menu that is a plain list of links; the script turns it into a toggle;
- a form that submits normally; the script adds live validation or AJAX;
<details>for disclosures,<dialog>for dialogs.
admin4's head script sets the class a4-js on <html> when scripts run, so stylesheets can draw the scripted state
only then. Do the same in your design (set a class from your head script file).
Checking
grep -rn -E "on(click|change|load|submit|keyup|keydown|mouseover)=|javascript:|<script>" extension/mytheme/design --include=*.tpland look at the browser console of every page type with the site's policy in report-only mode.
Related pages
- CSP-safe design
- Loading JavaScript
- No inline CSS
- Free documentation: this page is free to copy, translate and improve under the GNU FDL 1.3.
Become an Exponential Partner
Partners share their information, provide funding, contribute back and develop the products with us, and that is how Exponential grows. A partner website and dashboard are planned for mid-2027: please become a partner today to speed up our growth rate and land new features faster.