最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Window.matchmedia listener firing twice - Stack Overflow

programmeradmin0浏览0评论

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");
     });
});

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?

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?

Share Improve this question asked Jun 29, 2017 at 16:51 James HowellJames Howell 1,4525 gold badges26 silver badges46 bronze badges
Add a ment  | 

4 Answers 4

Reset to default 6 +50

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.

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.

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");
      }
    }

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'
发布评论

评论列表(0)

  1. 暂无评论