← toolkit.bot

JavaScript in EPUB 3: Scripted EPUBs, Interactivity, and Reader Support

June 12, 2026  ·  7 min read

EPUB 3 supports JavaScript — but with significant limitations compared to the web. Interactive EPUBs can include quizzes, collapsible content, and dynamic elements, but reader support is inconsistent and accessibility requires extra care. Here's what you need to know.

Enabling Scripting in EPUB 3

To use JavaScript in an EPUB, declare the content file as scripted in the OPF manifest:

<!-- In content.opf manifest -->
<item id="ch1" href="chapter01.xhtml"
      media-type="application/xhtml+xml"
      properties="scripted"/>

The scripted property tells reading systems this file contains scripts and may require JavaScript to render correctly. Reading systems that disable scripting will display a fallback.

Including JavaScript in XHTML

<!-- Inline script in XHTML -->
<script type="text/javascript">
//"<![CDATA[
  document.getElementById('toggle-btn').addEventListener('click', function() {
    var panel = document.getElementById('collapsible');
    panel.style.display = panel.style.display === 'none' ? 'block' : 'none';
  });
//]]>
</script>

<!-- External script -->
<script type="text/javascript" src="../scripts/interactive.js"></script>

The CDATA wrapping is required in XHTML to prevent XML parsers from treating JS operators (<, &) as XML markup.

Practical Interactive Patterns

Collapsible Section

<button id="toggle-btn" type="button"
        aria-expanded="false"
        aria-controls="collapsible">
  Show answer
</button>
<div id="collapsible" hidden>
  <p>The answer is 42.</p>
</div>

<script>
//"<![CDATA[
document.getElementById('toggle-btn').addEventListener('click', function() {
  var panel = document.getElementById('collapsible');
  var btn   = document.getElementById('toggle-btn');
  var shown = !panel.hidden;
  panel.hidden = shown;
  btn.setAttribute('aria-expanded', String(!shown));
});
//]]>
</script>

Simple Quiz

<form id="quiz">
  <fieldset>
    <legend>What does EPUB stand for?</legend>
    <label><input type="radio" name="q1" value="a"/> Electronic Publication</label>
    <label><input type="radio" name="q1" value="b"/> Extended PDF</label>
  </fieldset>
  <button type="submit">Check</button>
  <p id="result" aria-live="polite"></p>
</form>

<script>
//"<![CDATA[
document.getElementById('quiz').addEventListener('submit', function(e) {
  e.preventDefault();
  var answer = document.querySelector('input[name="q1"]:checked');
  document.getElementById('result').textContent =
    answer && answer.value === 'a' ? 'Correct!' : 'Try again.';
});
//]]>
</script>

Reading System Support Matrix

Reading SystemJS SupportNotes
Apple Books (iOS/macOS)YesBest JS support; full DOM access
Thorium ReaderYesChromium-based; good support
Kindle (app)PartialLimited; avoid complex DOM manipulation
Kindle (e-ink device)NoScripts silently ignored
Kobo (device)NoScripts silently ignored
Adobe Digital EditionsNoScripts stripped
Calibre ViewerYesGood for testing

Fallback Requirements

Because many readers don't support JavaScript, scripted EPUBs must degrade gracefully:

What JavaScript Cannot Do in EPUB

Convert PDF to EPUB3 free →