Media Query Dependent Event Listeners

One of the foundations of responsive web design is the use of media queries to alter the layout and style of a page depending on the browser’s dimensions. Something you see less often is altering the JavaScript being executed based on the result of a media query. This may be used to alter the style of the page but could also be used to improve performance.

Motivation

Recently I added a content menu to my longer blog posts which if your browser is sufficiently wide you will be able to see on the left of this article. As the page is scrolled the current section is highlighted in the menu. This effect is achieved through a scroll event listener used to determine the position on the page and update the menu style appropriately. To save space the menu is hidden when the browser width is reduced. During this state there is no need to continue to listen to and respond to scroll events so I wished for a way to remove and add event listeners dynamically depending on a media query.

window.matchMedia

It turns out there is a function which can help with this exact scenario! window.matchMedia may be passed a media query and it returns a MediaQueryList object which holds the result of the query. In addition, the returned object updates its values when the media query is triggered and provides an event listener to hook into these changes.

Removing and Adding the Listener

We can listen for changes in the result of the desired breakpoint using addListener.

const breakpoint = "1200px";
const widthMediaQuery = window.matchMedia(`(min-width: ${breakpoint})`);

widthMediaQuery.addListener(function(data){
  if (data.matches) {
    window.addEventListener("scroll", onScroll);
  }
  else {
    window.removeEventListener("scroll", onScroll);
  }
});
	
function onScroll() {
  // do something
}

This is great as now when the menu is not visible no work is wasted updating it!

But what happens if the menu currently shows a certain section, the user scrolls and changes section and then widens the browser again? The last section the user was on before resizing the browser will still be highlighted!

We can deal with this by immediately calling our event handler when adding the event listener back.

widthMediaQuery.addListener(function(data){
  if (data.matches) {
    window.addEventListener("scroll", onScroll);
    onScroll(); // Call the callback immediately
  }
  else {
    window.removeEventListener("scroll", onScroll);
  }
});

Conclusion

I wasn’t sure if there was a way to respond to media queries with JavaScript but was very pleased to learn about matchMedia! I think it could be used not only to improve performance for similar applications as described above but could also have some quite creative uses for dynamic styling via JavaScript.