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?
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");
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With