Monday, March 15, 2021

How to Animate on Scroll With Vanilla JavaScript

How to Animate on Scroll With Vanilla JavaScript

Making elements appear based on their scroll position is a very popular design choice when building web pages but it usually involves using a plugin or library. 

In this tutorial you’ll learn how to implement animation on scroll using vanilla JavaScript and CSS. The main advantage of using a custom implementation (as opposed to a library) is that it allows us to optimize our functions for accessibility and performance.

Scrolling Animation Demo

Here’s an example of how animating elements on scroll works:

Our implementation relies on CSS for animations and JavaScript to handle triggering the necessary styles. We’ll start by creating the layout.

1. Define the Page Structure

We’ll create the layout of our page using HTML and then assign a common class name to the elements we want to animate on scroll. This class name is what we’ll be targeting in JavaScript.

In the demo above, the elements were assigned the class name js-scroll so the HTML looks something like this:

2. Styling With CSS

CSS does a lot of the heavy-lifting as it determines the style of animation of each element. In this case, we’ll be animating the elements with the class name scrolled

This is an example of a simple fade-in animation:

With this code, any js-scroll element on the page is hidden with an opacity of 0 until the class name scrolled is applied to it.

3. Targeting Elements With JavaScript

Once we have our layout and styles, we’re going to create the JavaScript functions to assign the class name to the elements when they scroll into view. We’re also going to fade out the elements in JavaScript instead of CSS, as we want the elements to be visible in the event a browser does not have JavaScript enabled.

We’ll break down the logic like this:

  1. Get all js-scroll elements on the page
  2. Fade out elements
  3. Detect when the element is within the viewport
  4. Assign the scrolled class name to the element if it is in view.

Target Elements on The Page

We’ll target all the js-scroll elements on the page using the document.querySelectorAll() method. It should look like this:

Fade Out Elements

First, we need to remove the opacity:0 for .js-scroll in our CSS. Then we include this line in our JavaScript:

This allows the elements to have their default styling if JavaScript is disabled in the browser.

Detecting When an Element Is in View

We can detect when an element is in view of the user by determining if the distance of the element from the top of the page is less than the height of the visible part of the page.

In JavaScript, we use the getBoundingClientRect().top method to get an element’s distance from the top of the page, and window.innerHeight or document.documentElement.clientHeight to get the height of the viewport.

Source: Element.getBoundingClientRect() - Web APIs | MDN

We’ll create an elementInView function using the above logic:

We can modify this function to detect when the element has scrolled x pixels into the page, or detect when a percentage of the page has been scrolled.

In this case, the function returns true if the element has scrolled by the scrollOffset amount into the page. Modifying the logic gives us a different function for targeting elements based on percentage scroll.

An added benefit of a custom implementation is that we can define the logic to suit our specific needs.

Note: it’s possible to use the Intersection Observer API to achieve the same effect, however, at the time of writing this article, Intersection Observer is not supported in Internet Explorer so it doesn’t fit our bill of “works on all browsers”. 

Assign Class Name to Element

Now that we’re able to detect if our element has scrolled into the page, we’ll need to define a function to handle displaying the element–in this case we’re displaying the element by assigning the scrolled class name.

We’ll then combine our logic with the display function and use the forEach method to call the function on all js-scroll elements.

An optional feature is to reset the element to its default state when it’s no longer in view. We can do that by defining a hideScrollElement function and including it in an else statement to our above function:

Finally, we’ll pass the above method into a scroll event listener on the window so it runs whenever the user scrolls.

And viola, we’ve implemented all the functions we need to animate on scroll.

We can see how the logic works in this demo:

The complete code looks like this. JavaScript:

CSS:

4. More Animations With CSS

Let’s take a look at the first demo again:

We see that the elements appear with different animations. This was done by assigning different CSS animations to class names. The HTML for this demo looks like this:

Note: the classes next to the js-scroll class are what we target in CSS to handle the different animations. In our CSS stylesheet, we’ll have:

We don’t need to make any changes to the JavaScript code since the logic remains the same. This means we can have any number of different animations on a page without writing new functions.

5. Increasing Performance with Throttle

Whenever we include a function in a scroll listener, that function is called every time the user scrolls the page. Scrolling a page of 500px can cause a function to be called at least 50 times. If we’re trying to include a lot of elements on the page, this can cause our page to slow down significantly.

Throttle Function to the Rescue

We can reduce the number of times a function is called by using a “throttle function”. A throttle function is a higher order function that calls the function passed into it only once during a specified time interval.

It’s especially useful with scrolling events as we don’t need to detect every pixel scrolled by the user. For example, if we have a throttle function with a timer of 100ms, the function will only be called once for every 100ms the user scrolls.

A throttle function can be implemented in JavaScript  like this:

We can modify our window on scroll event listener to look like this

Now our handleScrollAnimation function is called every 250ms while the user is scrolling.

Here’s what the updated demo looks like:

6. Improving Accessibility

Performance isn’t the only requirement when implementing a custom feature; we also need to design for accessibility. Designing for accessibility means taking users’ choices and circumstances into consideration. Some users may not want to have animations at all, so we need to account for that.

The Reduced Motion Media Query

We can do that with the prefers-reduced-motion query and a JavaScript implementation.

“prefers-reduced-motion [...] is used to detect if the user has requested that the system minimize the amount of non-essential motion it uses” – MDN

Modifying our code above, the query would look like this in CSS:

With these lines of code, we ensure that the animated elements are always visible and the animation is turned off for all elements.

The prefers-reduced-motion query isn’t fully supported across all browsers so we can include a JavaScript fallback:

This way, if the user prefers reduced motion, the handleScrollAnimation function is never called at all.

That’s How to Animate on Scroll with JavaScript

We now have a highly performant, fully accessible implementation of the “animate on scroll” feature that works across all browsers!

More Practical JavaScript Tutorials


No comments:

Post a Comment