How it works
The life of a visit from the click to the end, which hooks fire and which classes appear.
Every navigation is a visit: an object that carries everything about it (where from, where to, what triggered it, how it animates). Every hook handler receives it. See The Visit object.
The steps
- Click. A listener on the document catches clicks on links. Links that should not be handled are left to the browser (see Which links are handled). Hook:
link:click. - Start.
visit:start. If another visit is still loading, it is aborted, including its network request. - Load.
page:loadfinds the page in the cache or fetches it (fetch:request). The address bar and history change right away. - Animate out.
animation:out:start,animation:out:await,animation:out:end. The default waits for CSS transitions and animations. - Replace.
content:replaceswaps the containers. Thencontent:scrollandpage:view. - Animate in.
animation:in:start,animation:in:await,animation:in:end. - End.
visit:endremoves the state classes.
If anything fails, visit:fail runs and, by default, the browser loads the page normally. A visit never leaves you on a broken page.
The exception is a failure after the new content is on screen (in content:scroll, page:view, the in-animation or visit:end). Reloading would fix nothing, so the error is logged in the console, the state classes are removed and the visit stops there. visit:fail does not run. A handler that throws at that point does not stop the other handlers of the same hook either.
When the browser brings a page back from its back/forward cache while a visit was half way, Pagefold cancels that visit and removes the state classes, so the page does not come back stuck.
Visits that are not animated skip steps 4 and 6 and fire animation:skip instead.
The visit as a timeline
The same steps, in order, with the classes on the html element at each moment. The animation hooks are highlighted.
-
1 Start
The old page is on screen.
-
visit:startThe visit exists. This is the moment to change it: its animation name, or whether it animates at all.
html no classes -
page:loadThe page starts loading, from the cache or from the network. It runs alongside the animation.
html no classes
-
-
2 Animate out
The old page is still on screen, animating out.
-
animation:out:startPagefold adds the state classes. Your CSS starts animating the old content out.
htmlis-changingis-animatingis-leaving -
animation:out:awaitwaitsPagefold waits for the CSS transitions to end. The JS plugin replaces this step with your own animation.
htmlis-changingis-animatingis-leaving -
animation:out:endThe old content is out. Nothing happens here by default.
htmlis-changingis-animatingis-leaving
-
-
3 Swap
The new content takes the place of the old one, hidden by the animation.
-
content:replaceThe containers are swapped and the title updates. The new content starts hidden, because is-animating is still on.
htmlis-changingis-animatingis-renderingis-leaving -
content:scrollThe scroll position is set: to the top, or to an anchor.
htmlis-changingis-animatingis-rendering -
page:viewThe new page is in place. This is where analytics page views are sent.
htmlis-changingis-animatingis-rendering
-
-
4 Animate in
The new page animates in.
-
animation:in:startPagefold removes is-animating. Your CSS transitions the new content in.
htmlis-changingis-renderingis-animating -
animation:in:awaitwaitsPagefold waits for the CSS transitions to end.
htmlis-changingis-rendering -
animation:in:endThe new content is in. Nothing happens here by default.
htmlis-changingis-rendering
-
-
5 End
Back to normal.
-
visit:endThe state classes are removed. The visit is over.
htmlis-changingis-rendering
-
State classes
CSS hooks into these classes on the html element:
| Class | When |
|---|---|
is-changing | During the whole transition |
is-animating | Animating out, and again right after the content is replaced; removed when animating in starts |
is-leaving | While the old content animates out |
is-rendering | After the content is replaced, until the visit ends |
is-popstate | On visits made with the back and forward buttons |
to-<name> | When the visit has an animation name, such as to-slide |
All of them are removed in visit:end. Classes of your own, even ones that start with to- or from-, are never touched.
Set animationScope: 'containers' to put the classes on the containers instead of the html element.
When a visit starts while another is running
- If the running visit is still loading, it is aborted. Its request is cancelled and its content never appears.
- If the running visit has already replaced the content, it finishes first. If several visits wait, only the last one runs.
The two cases side by side. In both, the visitor clicks a second link while the first visit is running:
Choosing what waits: queue
The second case is what the queue option decides:
queue | A visit that starts while another one is on screen |
|---|---|
'latest' (default) | Waits. If several wait, only the last one runs. |
'all' | Waits. Every one runs, in the order they were clicked. A visit that is still loading is not aborted either. |
'ignore' | Is dropped, the click does nothing. The back and forward buttons are never dropped. |
// A busy page does not take new clicks
new Pagefold({ queue: 'ignore' })
ignore is the simplest way to stop impatient double clicks from stacking animations. A dropped visit never starts: it gets the state ignored.
Requests
Pagefold fetches the destination page with fetch, sending X-Requested-With: pagefold and Accept: text/html. By default only a 500 response counts as a failure; a 404 page is shown like any other. Use failOnStatus to change that.