CSS 에서만 미디어 쿼리를 사용하지 않고 JS 에서도 programmatic 하게 미디어 쿼리를 사용할 필요가 생겨서 알아보던 중 window.matchMedia
가 그 역할을 한다는 것을 알게됬다.
matchMedia
matchMedia()
함수는 window
내장 메서드로, 파라미터로 string 형태의 미디어 쿼리를 받아서 현재 document
가 해당 미디어 쿼리를 충족하는지 알 수 있는 MediaQueryList
객체를 반환한다.
Examples
const mediaQueryList = window.matchMedia("(max-width: 600px)");
MediaQueryList
MediaQueryList(미디어 쿼리 리스트) 는 특정 document 에 적용된 media query 에 관한 정보를 포함하는 객체이다. MediaQueryLIst 는 matches
, media
두 프로퍼티를 포함한다.
-
MediaQueryList.matches
- 해당 document 가 주어진 미디어 쿼리를 만족하는지 여부로, boolean 값이다.
-
MediaQueryList.media
- 주어진 미디어 쿼리를 string 으로 serialize 한 값이다.
미디어 쿼리 리스트는 change
이벤트를 발생시킬 수 있다. 이 change
이벤트를 핸들링하는 이벤틑 리스너를 추가해주면 주어진 미디어 쿼리를 충족하는지 watch 할 수 있고, 결과적으로 programmatic 하게 미디어 쿼리를 사용할 수 있다.
Examples
// 600px 보다 크면 matches 가 false, 크면 true 가 되는 미디어 쿼리 리스트 생성
const mediaQueryList = window.matchMedia(`(max-width: 600px)`);
// document 가 미디어 쿼리를 충족시키는것에 대해 변화가 생기면 콘솔에 로깅한다
const changeHandler = (e: MediaQueryListEvent) => {
console.log("changed!");
};
// 600px 을 기준으로 document width 가 바뀔 때 마다 이벤트 핸들러를 호출한다
mediaQueryList.addEventListener("change", changeHandler);
React hook implementation
React에서 미디어 쿼리를 programmatic 하게 사용할 수 있도록 하는 useMediaQuery
라는 이름의 custom hook 을 만들고자 한다면 다음과 같이 사용할 수 있다.
/**
* window.matchMedia 를 활용하여 programmatically 하게 media query 를 활용하도록 하는 hook
* References: https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia
*/
import { useState, useEffect } from "react";
export interface UseMediaQueriesProps {
breakpoint: number;
}
const useMediaQueries = ({ breakpoint }: UseMediaQueriesProps) => {
/**
* 주어진 breakpoint 보다 화면이 작을 경우 match 를 true 로 초기화 시킨다
*/
const [mediaQuery, setmediaQuery] = useState<Partial<MediaQueryListEvent>>({
matches: window.innerWidth < breakpoint ? true : false,
media: "",
});
useEffect(() => {
/**
* window.matchMedia 에 쿼리를 전달하여 받은 mediaQueryList 를 가져오고
* change event handler 를 달아서 주어진 미디어 쿼리를 기반해서 viewport 가 breakpoint 사이에서 변화가 있다면
* 콜백 함수로 주어진 changeHandler 를 호출한다
* References: https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList
*/
const mediaQueryList = matchMedia(`(max-width: ${breakpoint}px)`);
const changeHandler = (e: MediaQueryListEvent) => {
setmediaQuery(e);
};
// for chrome, firefox and modern browsers
try {
mediaQueryList.addEventListener("change", changeHandler);
} catch (error) {
try {
// IE 는 onChange 를 아예 지원하지 않아서 지금은 deprecated 된 addListener 를 사용해야 한다
mediaQueryList.addListener(changeHandler);
} catch (error2) {
// 그래도 error 가 나면 sentry 로
console.error(error2);
}
}
return () => {
// for addEventListener
mediaQueryList.removeEventListener("change", changeHandler);
// for addListener
mediaQueryList.removeListener(changeHandler);
};
}, []);
return {
mediaQuery,
};
};
export default useMediaQueries;
Browser Supports
아래 그림에서 알 수 있듯이 MediaQueryList
는 기본적으로 대부분의 브라우저를 지원한다. 다만 IE 의 경우 change
이벤트를 발생시키지 않기 때문에, IE 에서는 addListener()
로 이벤트 핸들러를 달아주어야 한다.