使用 JavaScript 媒体查询

Avatar of Marko Ilic
Marko Ilic

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 200 美元的免费信用额度!

当您想到媒体查询时,首先想到的是什么? 可能是 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,其中包含更多详细信息。 数字表示浏览器从该版本及更高版本开始支持该功能。

桌面

ChromeFirefoxIEEdgeSafari
9610125.1

移动/平板电脑

Android ChromeAndroid FirefoxAndroidiOS Safari
12712735.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.innerWidthwindow.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 事件,因此这是一个代价高昂的操作! 查看一个**空页面**的性能影响,我们可以看到差异。

脚本增加了 157%!

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

这是 208 个 resize 事件与 6 个匹配的媒体事件。

即使我们忽略性能问题,resize 也有局限性,因为它不允许我们为打印和方向等内容编写高级媒体查询。 因此,虽然它确实通过允许我们匹配视口宽度来模拟“媒体查询”行为,但它无法匹配其他任何内容——并且我们知道真正的媒体查询能够做到更多。

结论

这就是 JavaScript 中媒体查询的概览! 我们探讨了 matchMedia() 如何使我们能够定义媒体条件,并检查了 MediaQueryList 对象,该对象使我们能够对这些条件进行一次性 (.matches) 和持久性 (addListener()) 检查,以便我们可以通过调用函数来响应更改 (.onchange)。

我们还看到了通过侦听窗口上的 resize 事件来执行操作的“旧”方法。 虽然它仍然被广泛使用,并且是响应 window.innerWidth 大小更改的完全合法的方法,但它无法对高级媒体条件执行检查。

为了结束本文,这里有一个旧方法无法实现的有用示例。 使用媒体查询,我将检查用户是否处于横向模式。 此方法在开发 HTML5 游戏时很常见,最好在移动设备上查看。