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

javascript - How can I force a matching window.matchMedia to execute on page load? - Stack Overflow

programmeradmin3浏览0评论

I noticed a difference between css media query definition and the javascript window.matchMedia media query definition:

The css rules are apllied initially to a loaded page.

The rules defined in javascript are not executed after the page load, but only after a new condition is entered.

An example:

I have two different pages with equivalent media query definitions, the first one defined in css and the second one defined in javascript:

the css version (defined in a style element):

@media (min-width: 401px) and (max-width: 600px) { body {background-color: red; } }
@media (min-width: 601px) and (max-width: 800px) { body {background-color: blue; } }

the javascript version (defined either globally or in a function called after body onload):

window.matchMedia("(min-width: 401px) and (max-width: 600px)")
.addListener(function(e) {
  if (e.matches) {
    document.body.style.background = "red";
  }
});

window.matchMedia("(min-width: 601px) and (max-width: 800px)")
.addListener(function(e) {
  if (e.matches) {
    document.body.style.background = "blue";
  }
});

When I load a page and the window is 700 px wide

  • the css version page is blue
  • the javascript version is white and changes its state only after a new condition is met, i.e. the window is sized below 601 px.

How can I force a matching window.matchMedia to execute on page load?

I noticed a difference between css media query definition and the javascript window.matchMedia media query definition:

The css rules are apllied initially to a loaded page.

The rules defined in javascript are not executed after the page load, but only after a new condition is entered.

An example:

I have two different pages with equivalent media query definitions, the first one defined in css and the second one defined in javascript:

the css version (defined in a style element):

@media (min-width: 401px) and (max-width: 600px) { body {background-color: red; } }
@media (min-width: 601px) and (max-width: 800px) { body {background-color: blue; } }

the javascript version (defined either globally or in a function called after body onload):

window.matchMedia("(min-width: 401px) and (max-width: 600px)")
.addListener(function(e) {
  if (e.matches) {
    document.body.style.background = "red";
  }
});

window.matchMedia("(min-width: 601px) and (max-width: 800px)")
.addListener(function(e) {
  if (e.matches) {
    document.body.style.background = "blue";
  }
});

When I load a page and the window is 700 px wide

  • the css version page is blue
  • the javascript version is white and changes its state only after a new condition is met, i.e. the window is sized below 601 px.

How can I force a matching window.matchMedia to execute on page load?

Share Improve this question asked Apr 23, 2018 at 20:49 Nathan PerigrethNathan Perigreth 1591 silver badge9 bronze badges 2
  • Check this stackoverflow.com/questions/1818474/… – Asons Commented Apr 23, 2018 at 21:29
  • I know window.onresize and body onload and I am aware that I could use those to solve my problem. But I assumed that this is not necessary. I believed window.matchMedia behaves the same as the css solution. I thought registering the queries once with callback functions would and should be enough. – Nathan Perigreth Commented Apr 24, 2018 at 7:02
Add a comment  | 

6 Answers 6

Reset to default 8

To fire a matchMedia on load, you could do like this instead (with a somewhat cleaner code base).

Stack snippet

<!DOCTYPE html>
<html>

  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <script>
      document.addEventListener("DOMContentLoaded", function(e) {
      
        // medias (as an array to make it a little easier to manage)
        var mqls = [
          window.matchMedia("(max-width: 400px)"),
          window.matchMedia("(min-width: 401px) and (max-width: 600px)"),
          window.matchMedia("(min-width: 601px) and (max-width: 800px)"),
          window.matchMedia("(min-width: 801px)")
        ]
        
        // event listeners
        for (var i=0; i<mqls.length; i++){
          mqls[i].addListener(mqh)
        }
        
        // matches methods
        function mqh(){
          if (mqls[0].matches) {
            console.log("CALLBACK (max-width: 400px)");
            document.body.style.background = "green";
          } else if (mqls[1].matches) {
            console.log("CALLBACK (max-width: 600px)");
            document.body.style.background = "red";
          } else if (mqls[2].matches) {
            console.log("CALLBACK (max-width: 800px)");
            document.body.style.background = "blue";
          } else if (mqls[3].matches) {
            console.log("CALLBACK (min-width: 801px)");        
            document.body.style.background = "gray";
          }
          console.log("window.innerWidth: " + window.innerWidth);
        }

        // call once on load
        mqh();
      });
      
    </script>
  </head>

  <body>
  </body>

</html>

Org. src: http://www.javascriptkit.com/javatutors/matchmediamultiple.shtml

I was facing the same problem today, and I've used the following solution inspired by Nathan:

const gate = 800

function listener(
  matches,
) {
  document.getElementById('tag').innerHTML = matches ? 'wider' : 'narrower'
}

window.onload=() => {
  const match = window.matchMedia(`(min-width: ${gate}px)`)
  match.addListener(e => listener(e.matches))
  listener(match.matches)
}
<h1>
  This window is 
  <span id='tag'></span>
  than 800px
</h1>

The core concept is to run your listener function once with MediaQueryList.matches passed as a parameter.

And if someone is trying to achieve this with a framework, do remember to register and trigger the listener during the component mount event.

A callback function bound to window.matchMedia is not called on page load.

A solution to the problem would be:

  • to define a function in which the media queries are explicitly checked via if(window.matchMedia("...").matches){
  • to call that function on page load via <body onload
  • to call that function on resize via window.onresize

This method worked for me

function media(args, callback) {
let mqList = window.matchMedia(args);
function handle(mediaEvent) {
    if (mediaEvent.matches) {
        callback(mediaEvent)
    } else {
        callback(mediaEvent)
    }
}
mqList.addEventListener('change', handle);
handle(mqList) // Initial check
}

Usage

media('(max-width: 920px)', (e) => {
    console.log(e.matches)//false/true
})

From https://css-tricks.com/working-with-javascript-media-queries/

If something is triggered by an event, there gotta be a way to fire that event manually.

const screenLg = window.matchMedia('(min-width: 601px) and (max-width: 800px)')

screenLg.addEventListener('change', ({ matches }) => {
  // your usual logic, without special cases for page load
})

// Fire the 'change' event, which you're subscribed on above, manually
screenLg.dispatchEvent(new MediaQueryListEvent('change', screenLg))

In React + TS you would want something like the following. Lodash's throttle is optional.

  const [isSmallScreen, setIsSmallScreen] = useState(false);

  useEffect(() => {
    const throttleUpdate = throttle((ev: MediaQueryListEvent) => setIsSmallScreen(ev.matches), 150);
    // 768 = md, https://ant.design/components/layout#breakpoint-width
    const matchMedia = window.matchMedia('(max-width: 768px)');
    setIsSmallScreen(matchMedia.matches);

    matchMedia.addEventListener('change', throttleUpdate);
    return () => matchMedia.removeEventListener('change', throttleUpdate);
  }, []);
  
  // ...
发布评论

评论列表(0)

  1. 暂无评论