媒体查询(Media Queries)
我们都知道媒体查询在响应式设计中扮演着一个重要的角色,它可以让我们在不同特性的设备上应用不同的 CSS 样式。一种常见的应用如下:
/* 在width不大于640的设备上,aside将被隐藏 */
@media (max-width: 640px) {
aside {
display: none;
}
}
其实在 JavaScript 中通过 MediaQueryList
接口,也可以进行媒体查询。
MediaQueryList 是什么?
MediaQueryList
(下面简称 MQL )是 CSSOM View Module 中定义的一个接口,一个 MQL 对象,本质上对应一条媒体查询语句,通过获取它的 matches 属性得到查询结果。
MQL 对象通过调用 window.matchMedia
方法获得,matchMedia
接受一个媒体查询语句作为参数,并返回对应的 MQL 对象。一个简单的例子如下:
const mql = window.matchMedia('(max-width: 640px)')
if (mql.matches) {
// width <= 640px
} else {
// width > 640px
}
mql 对象还提供了 change 事件接口,它拥有 addListener(handler)
和 removeListener(handler)
两个方法,用于监听和移除事件,这两个方法可以看作是 addEventListener('change', handler)
和 addEventListener('change', handler)
的简写。handler 是一个回调函数,传回一个 MediaQueryListEvent
作为参数。
const mql = window.matchMedia('(max-width: 640px)')
mql.addListener(mediaChangeHandler) // mql.onchange = mediaChangeHandler
function mediaChangeHandler (mqle) {
console.log(mqle.matches) // 更新后的boolean值
console.log(mqle.media) // 对应的查询字符串
}
它能做什么?
-
更灵活的响应式设计
CSS 中的媒体查询可以对相同的内容在不同的场景下作用不同的样式,而 JS 中的媒体查询可以让我们根据设备特性的变化直接修改内容,用一个简单点的例子来说就是 CSS 的媒体查询可以控制元素的显示与隐藏,而 MQL 可以直接控制元素是否存在。
-
在一定程度上代替 UA 检测
window.matchMedia('handheld') window.matchMedia('tv')
- 更简便的检测方案
需要注意啥?
-
MQL 对象是动态的
同 HTMLElement 上的 classList, childNodes 一样(不同的是 classList, childNodes 都是引用类型,而 matches 是基本类型),第一次通过 matchMedia 方法获取得到 mql 之后,后面媒体特性发生改变,mql.matches 拿到的就是最新的值,不需要再调用一次 matchMedia。
-
兼容性
MQL 比 CSS 中的媒体查询出来的晚一些,所以并不代表支持 CSS 媒体查询的浏览器就一定支持 MQL(当然支持 MQL 的浏览器支持 CSS Media Query 是没问题的),IE9 及以下不支持 MQL。对于 IE9, 可以使用 matchMedia polyfill,这个 polyfill 是基于 CSS 的 Media Queries 实现的,所以只能作用于支持 CSS Media Queries 的浏览器。
一个简单的例子
改变下面表格的宽度,查看变化
可以看到当屏幕宽度最大为 500px 时,月份显示的是头三个字母的缩写。这个例子的主要代码如下:
<table>
<caption>Days of Months</caption>
<thead>
<tr>
<th class="month">July</th>
<th class="month">August</th>
<th class="month">September</th>
<th class="month">October</th>
<th class="month">November</th>
<th class="month">December</th>
</tr>
</thead>
...
</table>
const daysOfMonths = {
"July": 31,
"August": 31,
"September": 30,
"October": 31,
"November": 30,
"December": 31
}
function mqHandler (mqle) {
const $months = document.querySelectorAll('.month')
let displayMonths = Object.keys(daysOfMonths)
if (mqle.matches) { // 最大宽度为 500px ,截取前 3 个字符
displayMonths = displayMonths.map(s => s.slice(0, 3))
}
$months.forEach(($el, i) => $el.textContent = displayMonths[i])
}
const mql = window.matchMedia('(max-width: 500px)')
mql.addListener(mqHandler)
mqHandler(mql) // 页面加载时初始化