javascriptecmascript-6matchmedia

Window.matchmedia listener firing twice


I'm trying to write some javascript which will change some values held within a JS config object at certain browser breakpoints.

I have stored the window.matchmedia tests within the config object and then I'm looping over this object's keys to add an event listener to each test like so:

Object.keys(config.mediaQueries).map((key) =>{
     config.mediaQueries[key].addListener(function(){
         console.log("breakpoint change");
     });
});

https://codepen.io/decodedcreative/pen/YQpNVO

However when the browser is resized and these listener callback functions run, they appear to run twice. Check the CodePen above with your Console open and you'll see what I mean.

Does anyone know what I've done wrong here?


Solution

  • To answer your direct question, you haven't done anything wrong. And the JS is doing exactly what it's supposed to do.

    trld There are 2 events firing but only one contains a matching media query.

    What's happening is that when the browser hits a breakpoint there are 2 events being logged. As an example, let's consider the case of when the browser is being resized from 1250px down to 1150px. When the window hits 1199px in width your function:

    Object.keys(config.mediaQueries).map((key) =>{
      config.mediaQueries[key].addListener(function(event){
        console.log("breakpoint change");
      });
    });
    

    will log 2 events. If you dive into this deeper and log the events with:

    Object.keys(config.mediaQueries).map((key) =>{
      config.mediaQueries[key].addListener(function(event){
        console.log(event);
      });
    });
    

    you'll see some more information on the media queries. I've boiled the event object down to the important props below.

    // First event
    matches: true
    media: "(max-width: 1199px) and (min-width: 992px)"
    
    // Second event
    matches: false
    media: "(min-width: 1200px)"
    

    What's happening here is 2 events are being logged, but only one of the events contains the matching query.

    So, if we wanted to improve your script for logging, you could check the matches property:

    Object.keys(config.mediaQueries).map((key) =>{
      config.mediaQueries[key].addListener(function(event){
        if (event.matches) { 
          console.log(event);
        }
      });
    });
    

    With this small change only the matching media query will be logged.