Creating a simple page transition using CSS and
JavaScript

Created: Sept 1st, 16'

Transitions are used everywhere on the web, popularized
with the advent of Ajax, where a "spinner" was often part of the
expected UI that shows up while content is being fetched. Today we
see transitions making an entrance (or exit for that matter) in all
types of scenarios, one of them being while a page is loading. You
saw an example of this while loading this page- reload to see
it again, or click here for another example of a page transition:.

In this tutorial, lets see how to build such a page transition
using JavaScript and CSS3, and along the way admire the power of
CSS3 transitions and keyframe animations in making the whole affair
extremely lightweight.

Building the basic interface/ markup

We'll begin by creating the UI for the page transition- a
DIV that covers and overlays the entire page:

The "#pageloader" DIV uses position:fixed
to ensure it spans every inch of the browser window, and
CSS Flexbox to center any children content added inside the DIV.

Note: For brevity certain CSS vendor prefixes are omitted
from the illustrated CSS, though they are present inside the
actual demo.

Moving on, we now need a spinner or loading animation to show
inside the DIV to indicate to visitors that the page is being
loaded. Instead of starting from scratch, we'll simply take
advantage of one of the pre-coded CSS3 based spinners from
Spinkit,
settling on the familiar 3 circles spinner:

Inisde the #pageloader DIV, we simply drop in the markup for the
spinner as provided straight into it, and call it a day (ok, half a
day, we still need to implement the JavaScript logic). And here's
the end result so far, a DIV that covers the whole page with a CSS3
spinner inside it:

Using JavaScript to determine when to hide the transition

All page transitions must end, and an appropriate time to bid it farewell is when the page has fully loaded. JavaScript's window.onload
event is perfect for this part of the puzzle, but only after some
tweaking. You see, some pages finish loading faster than others, and
for barren pages that take little time to fully load, it means the
page transition would simply flash by before being dismissed. On the
other hand there will also be pages that take a long time to
completely load, with which a transition that obscures the page the
whole time would frustrate users to no end To account for
these two extremes, we'll engineer the page transition so that its
dismissal while based on when the page has loaded, is constrained by
a minimum and maximum time.

Lets see the core JavaScript now that dismisses our document
transition after the page has loaded or 500 milliseconds, whichever
is longer, but no longer than 3000 milliseconds:

The code inside the window onload event takes care
of hiding the transition DIV once the document has loaded and
no sooner than the minimum minloadingtime setting value. We declare two variables, startTime, and elapsedTime, to keep
track of the starting time of the document transition, and when the
page has loaded, respectively. Inside the window onload event, we
calculate the time it took to load the document (elapsedTime) and
compare it to the minloadingtime setting to see if the transition
should be dismissed immediately at this point, or continue to show
until the minloadingtime setting duration has been reached:

On the other end of the aisle, the code maxloadingtimer = setTimeout(...) that
follows the onload code block is responsible for
dismissing the transition DIV if it's still visible past the maxloadingtime
setting value. Inside it, we first de-register the onload event function
(dismissonloadfunc) from firing inside the event before
proceeding to dismiss the page transition immediately.

To actually dismiss the page transition, a CSS class "dismissloader"
is added to the transition DIV that contains the necessary
styles to hide it gracefully via a fade out. We could have resorted
to using JavaScript to hide the transition DIV, though why not
leverage CSS3 to easily add additional effects to the process? Here is the "dismissloader"
class that should be defined on top of the existing CSS:

Notice how for the transition property, we set the
opacity
property to fade out over 1 second, while for the visibility
property, set it to disappear immediately (0s), but after a delay of
1 second, to let the opacity property finish transitioning
first. In CSS3, the visibility property is a "binary" property that
cannot be transitioned (it's either hidden or visible), but luckily
it can be delayed using CSS3. That's what we're doing here to make
sure the document transition not only fades out, but is totally
hidden at the end so the transition DIV doesn't interfere with the rest
of the document.

Checking for browser support for @keyframe animations, hiding
document scrollbars

Our page transition makes use of CSS transitions, keyframe
animations (for the spinner), and the
CSS
classList API (for adding/removing CSS classes), all of
which come with their own mixed bag of browser support. For our
purpose we want to make sure the browser supports @keyframe
animations and the classList API before giving the transition any
screen time; otherwise it should just be dismissed ASAP.

To check for CSS3 @keyframe animation support, we can simply
turn to
this snippet by MDN, while making sure the browser supports
Element.classList is even simpler. The following final JavaScript
code
incorporates the aforementioned checks to hide the page transition
immediately if any one of them fails. It also hides the document
scrollbars while the transition is visible:

At the top we include
the code for checking @keyframes support, which sets the
animation variable to true if the browser
supports CSS3 animations. The main body that dismisses the page
transition after a certain length of time is then only executed if
the browser supports both CSS animations and the
classList API (element.classList).
Otherwise, the transition DIV is hidden immediately.

Notice inside the main BODY how we're also adding a CSS class "hidescrollbar"
to the document root while the transition is visible just for good
measure; as the class name implies, this is to hide any document
scrollbars while the transition is in session. Here is the
corresponding CSS definition that makes that happen:

Bonus- implementing an "outro"
transition when the page unloads

Here's a bonus for the page transition we just looked at- adding
an "outro" effect that kicks in when the user leaves the
current page. A common outro effect entails fading out the
current page before the browser loads a new one.

Recall that we utilized window's onload event as a
trigger for the original, "intro" transition. For an outro, another
one of window's events comes in handy- the
beforeunload event. It fires just before the page is about
to be unloaded. Using this event, we can add a CSS class to the
document BODY that applies a transition just before the user is
whisked away to another page, such as fading the page out. Here is
the additional CSS and JavaScript that accomplishes this:

We've defined the .fadeout class to fade out the
BODY element over the course of 1 second when dynamically added to
that element. In reality the transition may not reach the finish
line before the page is unloaded and a new page takes its place, as
the "beforeunload" event doesn't wait for any
transition to finish before kicking in. In general, however, it
takes around .5s to 1s after clicking on a link before the current
page is overwritten, hence the 1s setting. The result is an easy outro effect, albeit slightly unrefined. Here is the original page
transition demo with such an outro transition added:

Click on one of the links at the bottom of the page above to exit
the page
and see the fade out effect.

Lastly, for those of you who cannot accept an outro transition
that can be cut short at times, a more precise approach would be to
use JavaScript to hijack outgoing links so they don't trigger
immediately, but rather, after the desired outro transition has
finished running. But that's for another day and another tutorial.