This paragraph is rendered inline by another snippet, via
story.renderSnippet().
<%
$(function () {
// A snippet-owned timer. Without a teardown hook this keeps
// firing forever after you navigate away, against DOM nodes
// that no longer exist.
const timer = setInterval(function () {
s.ticks++;
$("#ticks").text(s.ticks);
}, 200);
// Hand the cleanup to the engine: it runs once, when this
// snippet is left, and is then discarded.
story.onLeave(function () {
clearInterval(timer);
});
});
%>
Lifecycle
<%- story.renderSnippet("Banner") %>
A timer owned by this snippet has ticked 0
times.
Leave, and the next snippet checks whether it actually stopped:
Walk away
That link's text is wrapped in <strong> on purpose.
A handler reading event.target instead of
event.currentTarget would see the inner element and the
link would do nothing.
Story code has run <%- s.storyCodeRuns %> time(s) —
it must be 1, even though this snippet renders another one inline.
<%
$(function () {
const before = s.ticks;
$("#before").text(before);
// If onLeave did its job the counter is frozen; sample again
// after a second, by which point a live 200ms timer would have
// advanced it by about five.
setTimeout(function () {
const after = s.ticks;
$("#after").text(after);
const stopped = after === before;
$("#verdict")
.text(
stopped
? "PASS - the timer stopped when the snippet was left"
: "FAIL - the timer is still running (" +
(after - before) +
" ticks leaked)"
)
.addClass(stopped ? "pass" : "fail");
$("#log").html(
s.eventLog.map(function (line) {
return "<li><code>" + line + "</code></li>";
}).join("")
);
}, 1000);
});
%>
Check
Tick count on arrival: ?
Tick count one second later: ?
measuring…
Events seen so far
Story code has run <%- s.storyCodeRuns %> time(s).
Go back
/***
* Story code runs exactly once, at the very beginning of the story.
*
* Counting the runs here is not busywork: if a snippet renders another
* snippet inline (this example's "Start" does), a naive engine would run the
* story code a second time for the nested render. The counter below makes
* that visible.
*/
s.storyCodeRuns = (s.storyCodeRuns || 0) + 1;
s.ticks = 0;
s.eventLog = [];
/***
* The engine fires two events on `window` around every snippet change:
*
* iff:snippet:leaving - before the outgoing snippet's DOM is replaced
* iff:snippet:shown - after the incoming snippet has been rendered
*
* Both hand you the snippet object in question. Registering here (rather than
* in global code) means the handlers are bound once for the whole story.
*/
$(window).on("iff:snippet:leaving", function (_event, snippet) {
s.eventLog.push("leaving: " + (snippet ? snippet.name : "(nothing)"));
});
$(window).on("iff:snippet:shown", function (_event, snippet) {
s.eventLog.push("shown: " + snippet.name);
});