Media Queries in JavaScript

December 3, 2021
Frontend JavaScript

I’m working a project right now that has some auto-scroll/page-jumping behavior. Not a super advanced thing to do – take an element with an ID, append that ID to the URL, and boom, it’s automatically focused by the browser.

Go ahead, just type #second_p at the end of the URL in your address bar and this paragraph should be right at the top of the page.

We can trigger this behavior with JavaScript using the scrollTo function of the window object. Let’s markup our page, first.

<body> 
  <header>
    <!--a bunch of navigation>
  </header>
  <main>
    <div class='hero'>
      <!--probably an image and CTA, height of at least 100vh-->
    </div>
    <section id='main_content'>
      <!--finally, page content!-->
    </header>
  </main>
  <footer>
    <!--copywrong notice-->
  </footer>
</body>

The section with the ID set to main_content is what we’re trying to target here. To do that, we need to start by defining where we want the window to scroll and figuring out how we want the scrolling effect to behave. The window.scrollTo property will take an element’s x and y (vertical and horizontal) offsets (as top and left), then scroll the window to that offset. The function will also accept behavior, which can be smoothinstant, or auto. Passing smooth animates the scroll, while instant removes the animation and is a jump. auto lets the browser choose, but in my experience isn’t 100% reliable and usually chooses instance.

Implementation

To start, we’ll select the element and find out where it is on the page using element.getBoundingClientRect(), which will return a bunch of values relating to the dimensions and location relative to both the page and window. We’ll pass the top and left properties to the scrollTo functions, and pass 'smooth' for the behavior.

document.addEventListener('DOMContentLoaded', () => {
  var el = document.querySelector('#main_content');
  var rect = el.getBoundingClientRect();
  window.scrollTo({
    top: rect.top,
    left: rect.left,
    behavior: "smooth"
  });
})

Putting it Together

I put that all together in the demo below – with a 3 second delay on the scrolling action.

See the Pen Window Scroll Demo by Nate Northway (@the_Northway) on CodePen.

But even with that, there are some issues. When we animate something with CSS, we’re always able to check if the user wants to see animations using media queries, specifically checking for prefers-reduced-motion. Luckily, we can do that with JS too, and control the behavior value that is passed to window.scrollTo(). To do that, we need to start by defining a query and listening for settings to change without the page being reloaded. We can do that with window.matchMedia(), which accepts the media query we want to watch for as the argument. In return, we’ll get a MediaQueryList, or false if the media query doesn’t match. The MediaQueryList won’t update when the conditions change, so we will have to update the variable we store it to when it does. Fortunately, we can add an event listener to the variable to listen for changes! Let’s add that in to our code. There is a built in function that is part of the MediaQueryList, but that’s being deprecated. So I have opted to use the good ole’ addEventListener function.


document.addEventListener('DOMContentLoaded', () => {
  var mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
  var reduceMotion = mediaQuery.matches;
  mediaQuery.addEventListener("change", () => {
    reduceMotion = mediaQuery.matches;
  })
  var el = document.querySelector('#main_content');
  var rect = el.getBoundingClientRect();
  window.scrollTo({
    top: rect.top,
    left: rect.left,
    behavior: mediaQuery.matches?"instant":"smooth"
  });
})

All of that is in the demo below, again with a 3 second delay.

See the Pen Window Scroll with Media Query in JS by Nate Northway (@the_Northway) on CodePen.

We can use this for a whole lot of media queries that change behavior based on screen size, accessibility settings, and more. We should use this to listen for changes to screen size and accessibility settings, especially when animations are part of the page and especially when those animations aren’t controlled by CSS exclusively.

No Comments...yet

Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post

Let’s Build A More Accessible Collapsible

From September 6, 2021

Collapsible elements are useful pieces of UI that make digesting lots of content easier. But they’re often not accessible.

Read This Article
Next Post

Gradients in CSS

From January 16, 2022

A *lot* is possible with gradients in CSS – from the classic linear, to conic, it’s all SUPER fun stuff.

Read This Article