Pagefold

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

  1. 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.
  2. Start. visit:start. If another visit is still loading, it is aborted, including its network request.
  3. Load. page:load finds the page in the cache or fetches it (fetch:request). The address bar and history change right away.
  4. Animate out. animation:out:start, animation:out:await, animation:out:end. The default waits for CSS transitions and animations.
  5. Replace. content:replace swaps the containers. Then content:scroll and page:view.
  6. Animate in. animation:in:start, animation:in:await, animation:in:end.
  7. End. visit:end removes 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. 1

    Start

    The old page is on screen.

    1. visit:start

      The visit exists. This is the moment to change it: its animation name, or whether it animates at all.

      html no classes
    2. page:load

      The page starts loading, from the cache or from the network. It runs alongside the animation.

      html no classes
  2. 2

    Animate out

    The old page is still on screen, animating out.

    1. animation:out:start

      Pagefold adds the state classes. Your CSS starts animating the old content out.

      html is-changingis-animatingis-leaving
    2. animation:out:await waits

      Pagefold waits for the CSS transitions to end. The JS plugin replaces this step with your own animation.

      html is-changingis-animatingis-leaving
    3. animation:out:end

      The old content is out. Nothing happens here by default.

      html is-changingis-animatingis-leaving
  3. 3

    Swap

    The new content takes the place of the old one, hidden by the animation.

    1. content:replace

      The containers are swapped and the title updates. The new content starts hidden, because is-animating is still on.

      html is-changingis-animatingis-rendering is-leaving
    2. content:scroll

      The scroll position is set: to the top, or to an anchor.

      html is-changingis-animatingis-rendering
    3. page:view

      The new page is in place. This is where analytics page views are sent.

      html is-changingis-animatingis-rendering
  4. 4

    Animate in

    The new page animates in.

    1. animation:in:start

      Pagefold removes is-animating. Your CSS transitions the new content in.

      html is-changingis-rendering is-animating
    2. animation:in:await waits

      Pagefold waits for the CSS transitions to end.

      html is-changingis-rendering
    3. animation:in:end

      The new content is in. Nothing happens here by default.

      html is-changingis-rendering
  5. 5

    End

    Back to normal.

    1. visit:end

      The state classes are removed. The visit is over.

      html is-changingis-rendering

State classes

CSS hooks into these classes on the html element:

ClassWhen
is-changingDuring the whole transition
is-animatingAnimating out, and again right after the content is replaced; removed when animating in starts
is-leavingWhile the old content animates out
is-renderingAfter the content is replaced, until the visit ends
is-popstateOn 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:

Visit A (loading)
outload…aborted
Visit B
outswapin
Click B while A is still loading: A is stopped and its request is cancelled. B starts right away.
Visit A (swapped)
outswapin
Visit B
outswapin
Click B after A swapped the content: A finishes, then B runs. If C and D are also waiting, only D runs.

Choosing what waits: queue

The second case is what the queue option decides:

queueA 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.