当您想到媒体查询时,首先想到的是什么? 可能是 CSS 文件中类似这样的内容
body {
background-color: plum;
}
@media (min-width: 768px) {
body {
background-color: tomato;
}
}
CSS 媒体查询是任何响应式设计的核心要素。 它们是将不同样式应用于不同上下文的绝佳方式,无论它是基于视口大小、运动偏好、首选颜色方案、特定交互,甚至像打印机、电视和投影仪等特定设备,等等。
但您知道 JavaScript 也有媒体查询吗? 没错! 我们在 JavaScript 中可能不太常看到它们,但多年来我确实发现了一些有用的用例,用于创建响应式插件,例如滑块。 例如,在特定分辨率下,您可能需要重新绘制和重新计算滑块项目。
在 JavaScript 中使用媒体查询与在 CSS 中使用媒体查询非常不同,尽管概念相似:匹配某些条件并应用某些内容。
使用 matchMedia()
要确定文档是否与 JavaScript 中的媒体查询字符串匹配,我们使用 matchMedia()
方法。 即使它正式属于 CSS 对象模型视图模块规范(处于工作草案状态),但它在浏览器中的支持度也非常好,早在 Internet Explorer 10 就开始支持,全球覆盖率高达 98.6%。
此浏览器支持数据来自 Caniuse,其中包含更多详细信息。 数字表示浏览器从该版本及更高版本开始支持该功能。
桌面
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
9 | 6 | 10 | 12 | 5.1 |
移动/平板电脑
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
127 | 127 | 3 | 5.0-5.1 |
用法与 CSS 媒体查询几乎相同。 我们将媒体查询字符串传递给 matchMedia(
),然后检查 .matches
属性。
// Define the query
const mediaQuery = window.matchMedia('(min-width: 768px)')
定义的媒体查询将返回一个 MediaQueryList
对象。 它是一个存储有关媒体查询的信息的对象,我们需要的主要属性是 .matches
。 这是一个只读布尔属性,如果文档与媒体查询匹配,则返回 true
。
// Create a media condition that targets viewports at least 768px wide
const mediaQuery = window.matchMedia('(min-width: 768px)')
// Check if the media query is true
if (mediaQuery.matches) {
// Then trigger an alert
alert('Media Query Matched!')
}
这是在 JavaScript 中匹配媒体条件的基本用法。 我们创建一个匹配条件 (matchMedia()
),它返回一个对象 (MediaQueryList
),对其进行检查 (.matches
),然后如果条件计算结果为 true,则执行某些操作。 与 CSS 并不完全不同!
但还有更多内容。 例如,如果我们将窗口大小更改为低于我们的目标窗口大小,则没有任何内容会像 CSS 一样开箱即用地更新。 这是因为 .matches
非常适合一次性即时检查,但无法持续检查更改。 这意味着我们需要…
侦听更改
MediaQueryList 有一个 addListener()
(以及随后的 removeListener()
)方法,它接受一个回调函数(由 .onchange
事件表示),当媒体查询状态发生更改时会调用该函数。 换句话说,当条件发生变化时,我们可以触发其他函数,从而使我们能够“响应”更新后的条件。
// Create a condition that targets viewports at least 768px wide
const mediaQuery = window.matchMedia('(min-width: 768px)')
function handleTabletChange(e) {
// Check if the media query is true
if (e.matches) {
// Then log the following message to the console
console.log('Media Query Matched!')
}
}
// Register event listener
mediaQuery.addListener(handleTabletChange)
// Initial check
handleTabletChange(mediaQuery)
matchMedia()
和 MediaQueryList
的组合使我们不仅能够匹配 CSS 提供的媒体条件,而且能够主动响应更新后的条件。
当您使用 addListener()
注册事件侦听器时,它不会立即触发。 我们需要手动调用事件处理程序函数并将媒体查询作为参数传递。
旧方法
为了提供上下文——以及一点怀旧之情——我想介绍一下在 JavaScript 中执行“媒体查询”的旧方法,但它仍然很流行(是的,这里引号很重要)。 最常见的方法是绑定一个 resize
事件侦听器,该侦听器检查 window.innerWidth
或 window.innerHeight
。
您仍然会在实际环境中看到类似这样的代码
function checkMediaQuery() {
// If the inner width of the window is greater then 768px
if (window.innerWidth > 768) {
// Then log this message to the console
console.log('Media Query Matched!')
}
}
// Add a listener for when the window resizes
window.addEventListener('resize', checkMediaQuery);
由于每次浏览器调整大小时都会调用 resize 事件,因此这是一个代价高昂的操作! 查看一个**空页面**的性能影响,我们可以看到差异。

一种更简单的方法是使用控制台日志来查看差异。

即使我们忽略性能问题,resize 也有局限性,因为它不允许我们为打印和方向等内容编写高级媒体查询。 因此,虽然它确实通过允许我们匹配视口宽度来模拟“媒体查询”行为,但它无法匹配其他任何内容——并且我们知道真正的媒体查询能够做到更多。
结论
这就是 JavaScript 中媒体查询的概览! 我们探讨了 matchMedia()
如何使我们能够定义媒体条件,并检查了 MediaQueryList
对象,该对象使我们能够对这些条件进行一次性 (.matches
) 和持久性 (addListener()
) 检查,以便我们可以通过调用函数来响应更改 (.onchange
)。
我们还看到了通过侦听窗口上的 resize
事件来执行操作的“旧”方法。 虽然它仍然被广泛使用,并且是响应 window.innerWidth
大小更改的完全合法的方法,但它无法对高级媒体条件执行检查。
为了结束本文,这里有一个旧方法无法实现的有用示例。 使用媒体查询,我将检查用户是否处于横向模式。 此方法在开发 HTML5 游戏时很常见,最好在移动设备上查看。
感谢您解释这种使用 js 媒体查询的有用方法。
我喜欢它,我一直使用“旧”方法
.match 没有必要检查布尔值
如何在 React 中使用它? 我一直在寻找一种优雅的方法来使我的组件响应,而不使用 css。 我不想自称是 React 开发人员,却仍然使用 css 媒体查询而不是全部使用 js。 请就此发表意见。 谢谢
您可以使用
useState
钩子和将状态设置函数绑定到事件侦听器。 因此,它将在每次更改时设置值,并且状态将具有反应性。 只需在useEffect
钩子中添加初始检查和设置。只是一个想法,我不太使用 React。
我是一名 React 开发人员,但我仍然认为使用 CSS 媒体查询而不是 JS 的性能优于等待浏览器/组件挂载并在之后获取视口宽度/高度。
毫无疑问哪个更好,它们不应该进行比较。 这只是解决旧问题的一种新的优化方法,即
resize
事件加上检查浏览器的宽度。今天我了解了与 match media 相关的事件……感谢 Amit 提供的信息……我认为方向媒体本身存在一些问题……当虚拟键盘打开时,它无法正常工作……。
没错,不幸的是,这是一个旧的 CSS 问题。 这是原因说明。
如果你查看 W3 提供的规范,它会说
规范:https://www.w3.org/TR/css3-mediaqueries/#orientation
这就是为什么这个问题有时会发生。可以通过编写不同的查询来改进这一点,例如使用
min-aspect-ratio
基于你的帖子,我创建了一个小模块,可以像我们在开发中通常需要的那样以一种好的方式使用。
https://github.com/bzaman/JavaScript-Media-Queries/blob/master/README.md
感谢这篇文章。这里可以添加一些改进。与其使用标记为已弃用的特定方法
addListener
,我们可以使用原生事件接口,并以这种方式设置它mediaQuery.addEventListener('change', handleTabletChange)
。addListener
经常被提及已弃用,但据我所知,它实际上并没有弃用。即使你查看 MDN 页面,它也没有将其标记为已弃用。查看 CSSWG dom,他们指出
这就是我使用它的方式,基本上是一个向后兼容的别名。
但你并没有错,添加一个用于
change
的事件监听器实际上就是正在发生的事情,并且你的代码将起作用。@marco 它现在是:https://mdn.org.cn/en-US/docs/Web/API/MediaQueryList/addListener#browser_compatibility
抱歉发布了这么多垃圾邮件,但我现在才注意到
addListener
在 IE10 及更高版本中受支持,而addEventListener
也在 IE9 中受支持,这似乎实际上扩大了支持范围。我是不是错过了什么,或者调整大小总数是 3498?不过文章写得很好。
嗯,有人眼力不错。抓住了重点。这是因为数字是浮点数,我们只看到了整数。当求和时,它加起来变成了 1(我猜)。
使用 CSS 变量怎么样?类似这样
@media (min-width: 450px) {
:root {
–actualMedia: small;
}
}
然后在 JS 中读取 –actualMedia 变量的值
if (getComputedStyle(document.documentElement)
.getPropertyValue(‘–actualMedia’)
.trim() === “small”) {
// …
}
我之前见过这个解决方案。当时它类似于
body:after {content: "mq-small"
,而使用 CSS 自定义属性的新方法更好,但在 IE 11 中不起作用。但我不知道从性能角度来看哪个解决方案更好?前段时间我也发现了这一点,我创建了一个库来帮助轻松地在所有平台上使用 matchMedia,如果你想查看它:https://github.com/enmanuelduran/mediaquerysensor
函数中的
e
是什么?@Mohammed 那是事件对象,在本例中是
mediaQuery
返回的对象。在 @media 的标准用法中,我们可以执行以下操作
@media only screen and (max-width: 479px) and (orientation: portrait) {
// CSS
}
@media only screen and (min-width: 480px) and (max-width: 768px) {
// CSS
}
@media only screen and (min-width: 769px) and (max-width: 1280px) {
// CSS
}
@media only screen and (min-width: 1281px) and (max-width: 1599px) {
// CSS
}
使用你的“matchMedia”方法可以做类似的事情吗?你的示例只显示了一个分辨率(min-width: 768px),所以我想知道这是否是一个限制。想知道这是否可行。谢谢!
感谢这篇文章,它今天帮了我很大的忙。
我建议更新一下,因为“addListener”已弃用。
再次感谢,并致以最诚挚的祝福。