Window.matchmedia listener firing twice
Asked Answered
D

4

6

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?

Disparagement answered 29/6, 2017 at 16:51 Comment(0)
L
6

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.

Lowenstern answered 1/7, 2017 at 19:36 Comment(1)
Awesome. Thanks for your help.Disparagement
Z
2

If you change your console.log("breakpoint change"); to console.log(key, "breakpoint change");, you'll see that both the xs and s queries are being triggered when you manually resize the browser window.

If you only want your callback to fire once within a time window, you'll need to throttle the events or achieve the behavior in some other way.

Edit: Tomasz Bubała and Brett DeWoody's answers both point to the event.matches property as the proper and more .matchMedia specific solution to this problem.

Zucchetto answered 1/7, 2017 at 19:0 Comment(0)
S
1

Seems like the event is firing just before resizing and right after resizing. Add an if statement if you want to log "breakpoint" change right after it changed.

Edit: Noah Freitas might be right about the event firing for 2 keys when resizing. Still e.matches returns true if it matches the key so it should work fine

    function(e) {
      if(e.matches) {
        console.log("breakpoint change");
      }
    }
Sharondasharos answered 1/7, 2017 at 19:0 Comment(0)
R
0

Not sure, if this is already the full answer. But you are right. The callback is fireing twice - for two different keys.

I modified your example, so it outputs the key:

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

The resulting logging output always produces two lines:

breakpoint change key:'m'
breakpoint change key:'l'

Or for decreased width:

breakpoint change key:'s'
breakpoint change key:'m'
Rendition answered 1/7, 2017 at 19:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.