RecipesChapter navigation
Chapter navigation
Build a clickable chapter list that drives playback through the Player API.
Long-form videos benefit from a chapter list that lets viewers jump straight to the section they care about. Keep your chapter metadata alongside the page and seek through the Player API.
Define your chapters
const chapters = [
{ id: "intro", title: "Introduction", start: 0 },
{ id: "setup", title: "Setting up", start: 42 },
{ id: "demo", title: "Live demo", start: 187 },
{ id: "qa", title: "Q&A", start: 354 },
]Render the navigation
<aside class="chapters">
<h2>Chapters</h2>
<ol id="chapter-list"></ol>
</aside>const list = document.getElementById("chapter-list")
list.innerHTML = chapters
.map(
(chapter) =>
`<li><button data-start="${chapter.start}">${chapter.title}</button></li>`,
)
.join("")
list.addEventListener("click", (event) => {
const target = event.target.closest("button")
if (!target) return
const start = Number(target.dataset.start)
window.Moviie.seek(start)
window.Moviie.play()
})Highlight the current chapter
Bind to timeupdate (throttled — it fires four times per second) and
update the active chapter when crossing the next start time:
let activeId = null
window.Moviie.on("timeupdate", ({ currentTime }) => {
const current = [...chapters]
.reverse()
.find((chapter) => currentTime >= chapter.start)
if (current && current.id !== activeId) {
activeId = current.id
document
.querySelectorAll("#chapter-list [data-start]")
.forEach((node) => node.classList.toggle("active", node.dataset.start == current.start))
}
})Tips
- Pair the chapter list with deep links: read
?chapter=demofrom the URL on first load and seek accordingly. - Persist the last-watched chapter alongside the progress recipe so the next session resumes inside the right section.