当你想到媒体查询时,你首先想到的是什么?也许在CSS文件中是这样的:
代码语言:javascript复制body {
background-color: plum;
}
@media (min-width: 768px) {
body {
background-color: tomato;
}
}
CSS媒体查询是任何响应式设计的核心成分。它们是将不同样式应用到不同上下文的好方法,无论它是基于视口大小、运动偏好、首选的配色方案、特定的交互,甚至是特定的设备,如打印机、电视和投影仪等。
但你知道我们对JavaScript也有媒体查询吗? 我们可能在JavaScript中并不经常看到它们,但在过去的几年里,我发现它们对于创建响应式插件(如滑块)很有帮助。例如,在某个分辨率下,您可能需要重新绘制和重新计算滑块项目。
在JavaScript中处理媒体查询与在CSS中处理媒体查询是非常不同的,尽管概念是相似的:匹配一些条件并应用一些东西。
Using matchMedia()
为了确定文档是否与JavaScript中的媒体查询字符串匹配,我们使用matchMedia()
方法。尽管它是CSS对象模型视图模块规范的正式组成部分,但浏览器对它的支持可以追溯到ie10
,全球覆盖率为98.6%
。
其用法几乎与CSS媒体查询相同。我们将媒体查询字符串传递给matchMedia()
,然后检查.matches
属性。
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中匹配媒体条件的基本用法。我们创建一个返回对象(MediaQueryList)
的匹配条件(matchMedia())
,对其进行检查(.matches)
,然后如果条件的计算结果为真,就执行操作。并不是完全不像CSS!
但还有更多。例如,如果我们改变窗口大小低于我们的目标窗口大小,没有任何更新方式与CSS直接开箱即用。这是因为.matches非常适合一次性的即时检查,但无法持续地检查更改。这意味着我们需要……
监听 changes 事件
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提供的媒体条件,而且还能够积极响应更新的条件。
练习
代码语言:javascript复制<div class="info">Device Info:
代码语言:javascript复制const outputElement = document.getElementById("info");
const smallDevice = window.matchMedia("(min-width: 576px)");
smallDevice.addListener(handleDeviceChange);
function handleDeviceChange(e) {
if (e.matches) outputElement.textContent = "Bigger Than Mobile";
else outputElement.textContent = "Mobile";
}
// Run it initially
handleDeviceChange(smallDevice);
接着是 CSS:
代码语言:javascript复制.info {
font-size: 18px;
font-weight: bold;
}
/*
CSS USED FOR THE TEMPLATE
*/
* {
box-sizing: border-box;
}
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
color: #252631;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
height: 100vh;
background-color: #efefef;
}
.desc {
max-width: 400px;
opacity: 0.6;
text-align: center;
line-height: 1.6;
}
最终如下结果:
老方法存在的缺陷
出于上下文考虑——还有一点怀旧之情——我想介绍一下用JavaScript进行“媒体查询”的老方法(是的,这些引号在这里很重要)。最常见的方法是绑定一个检查window.innerWidth
或 window.innerHeight
的 resize
事件监听器。
你仍然会看到这样的方式:
代码语言:javascript复制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
事件的“老”方法。尽管它仍然被广泛使用,并且是响应窗口大小变化的一种完全合法的方式。但是 innerWidth
,无法对高级媒体条件执行检查。
为了完成本文,这里有一个用旧方法无法实现的有用示例。使用媒体查询,我将检查用户是否处于横向模式。这种方法在开发HTML5游戏时很常见,在移动设备上观看效果最好:
结论
这就是JavaScript中的媒体查询!我们探索了matchMedia()
如何允许我们定义媒体条件,并检查了MediaQueryList
对象,该对象允许我们对这些条件进行一次性(.matches
)和持久性(addListener()
)检查,以便我们可以通过调用函数来响应更改(.onchange
)。
我们还看到了侦听窗口上的resize
事件的“老”方法。尽管它仍然被广泛使用,并且是响应窗口大小变化的一种完全合法的方式。但是 innerWidth
,无法对高级媒体条件执行检查。
为了完成本文,这里有一个用旧方法无法实现的有用示例。使用媒体查询,我将检查用户是否处于横向模式。这种方法在开发HTML5游戏时很常见,在移动设备上观看效果最好。
HTML:
代码语言:javascript复制HTML SCSS JSResult Skip Results Iframe
EDIT ON
<div id="notice">
<svg viewbox="0 0 512 512.001" xmlns="http://www.w3.org/2000/svg">
<path d="m187.886719 7.566406h-170.5625c-5.300781 0-9.601563 4.296875-9.601563 9.601563v349.027343c0 5.304688 4.296875 9.601563 9.601563 9.601563h170.5625c5.300781 0 9.601562-4.296875 9.601562-9.601563v-349.027343c0-5.304688-4.300781-9.601563-9.601562-9.601563zm0 0" fill="#8ac2ff"></path>
<path d="m136.492188 324.402344v170.5625c0 5.300781 4.296874 9.601562 9.601562 9.601562h349.027344c5.300781 0 9.601562-4.300781 9.601562-9.601562v-170.5625c0-5.300782-4.296875-9.601563-9.601562-9.601563h-349.027344c-5.304688 0-9.601562 4.300781-9.601562 9.601563zm0 0" fill="#8ac2ff"></path>
<g fill="#54a0ff">
<path d="m37.726562 366.195312v-349.027343c0-5.304688 4.296876-9.601563 9.597657-9.601563h-30c-5.304688 0-9.601563 4.296875-9.601563 9.601563v349.027343c0 5.304688 4.296875 9.601563 9.601563 9.601563h30c-5.300781 0-9.597657-4.296875-9.597657-9.601563zm0 0"></path>
<path d="m166.492188 494.964844v-170.5625c0-5.300782 4.296874-9.601563 9.601562-9.601563h-30.003906c-5.300782 0-9.597656 4.296875-9.597656 9.601563v170.5625c0 5.300781 4.296874 9.601562 9.597656 9.601562h30.003906c-5.300781 0-9.601562-4.300781-9.601562-9.601562zm0 0"></path>
<path d="m146.09375 314.800781c-5.304688 0-9.601562 4.300781-9.601562 9.601563v51.394531h51.394531c5.300781 0 9.601562-4.296875 9.601562-9.601563v-51.394531zm0 0"></path>
</g>
<path d="m166.492188 324.402344c0-5.300782 4.296874-9.601563 9.601562-9.601563h-30.003906c-5.300782 0-9.597656 4.296875-9.597656 9.601563v51.394531h30zm0 0" fill="#338def"></path>
<path d="m463.453125 245.71875h-52.003906c-2.519531 0-3.777344 3.042969-2 4.824219l26.003906 26c1.101563 1.105469 2.890625 1.105469 3.996094 0l26-26c1.78125-1.78125.519531-4.824219-1.996094-4.824219zm0 0" fill="#e4eaf8"></path>
<path d="m261.347656 46.351562-26 26c-1.105468 1.105469-1.105468 2.894532 0 4l26 26c1.78125 1.78125 4.824219.519532 4.824219-2v-52c0-2.519531-3.042969-3.777343-4.824219-2zm0 0" fill="#e4eaf8"></path>
<path d="m431.683594 250.542969c-1.777344-1.78125-.519532-4.824219 2-4.824219h-22.234375c-2.519531 0-3.777344 3.042969-2 4.824219l26.003906 26c1.101563 1.105469 2.890625 1.105469 3.996094 0l9.117187-9.117188zm0 0" fill="#c7d2e5"></path>
<path d="m257.582031 76.351562c-1.105469-1.105468-1.105469-2.894531 0-4l8.589844-8.589843v-15.410157c0-2.519531-3.046875-3.78125-4.824219-2l-26 26c-1.105468 1.105469-1.105468 2.894532 0 4l26 26c1.78125 1.78125 4.824219.519532 4.824219-2v-15.410156zm0 0" fill="#c7d2e5"></path>
<path d="m404.410156 255.78125 26.003906 26c4.027344 4.027344 10.574219 4.027344 14.601563 0l26.003906-26c6.488281-6.492188 1.886719-17.628906-7.304687-17.628906h-18.640625c-3.5-92.816406-78.332031-167.679688-171.136719-171.222656v-18.644532c0-9.175781-11.128906-13.800781-17.628906-7.304687l-26 26.003906c-4.027344 4.023437-4.027344 10.578125 0 14.605469l26 25.996094c6.484375 6.488281 17.628906 1.910156 17.628906-7.300782v-18.34375c84.53125 3.527344 152.644531 71.667969 156.125 156.210938h-18.347656c-9.191406 0-13.792969 11.140625-7.304688 17.628906zm-145.476562-166.78125-14.714844-14.714844 14.714844-14.714844zm193.496094 164.152344-14.714844 14.71875-14.714844-14.71875zm0 0"></path>
<path d="m494.898438 307.234375h-290.132813v-98.117187c0-4.144532-3.359375-7.5-7.5-7.5-4.144531 0-7.5 3.355468-7.5 7.5v98.117187h-43.894531c-9.429688 0-17.101563 7.671875-17.101563 17.101563v43.894531h-111.667969c-1.15625 0-2.101562-.941407-2.101562-2.097657v-349.03125c0-1.160156.945312-2.101562 2.101562-2.101562h170.5625c1.15625 0 2.101563.941406 2.101563 2.101562v157.015626c0 4.140624 3.355469 7.5 7.5 7.5s7.5-3.359376 7.5-7.5v-157.015626c0-9.429687-7.671875-17.101562-17.101563-17.101562h-170.5625c-9.429687 0-17.101562 7.671875-17.101562 17.101562v349.03125c0 9.429688 7.671875 17.101563 17.101562 17.101563h111.667969v111.664063c0 9.429687 7.671875 17.101562 17.101563 17.101562h155.195312c4.140625 0 7.5-3.355469 7.5-7.5s-3.359375-7.5-7.5-7.5h-155.195312c-1.160156 0-2.101563-.941406-2.101563-2.101562v-111.667969h43.894531c9.429688 0 17.101563-7.667969 17.101563-17.101563v-43.894531h290.132813c1.15625 0 2.101562.941406 2.101562 2.101563v170.5625c0 1.15625-.941406 2.101562-2.101562 2.101562h-158.832032c-4.144531 0-7.5 3.355469-7.5 7.5s3.355469 7.5 7.5 7.5h158.832032c9.429687 0 17.101562-7.671875 17.101562-17.101562v-170.5625c0-9.429688-7.671875-17.101563-17.101562-17.101563zm-305.132813 58.894531c0 1.160156-.945313 2.101563-2.101563 2.101563h-43.894531v-43.894531c0-1.15625.941407-2.101563 2.101563-2.101563h43.894531zm0 0"></path>
</svg>
<p>Please rotate the device.</p>
</div>
<div id="start">
<mark>Start</mark>
</div>
CSS:
代码语言:javascript复制#notice {
text-align: center;
}
a {
color: #000;
}
footer {
position: absolute;
bottom: 10px;
opacity: 0.4;
}
/*
Template CSS
*/
* {
box-sizing: border-box;
}
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
color: #252631;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
height: 100vh;
background-color: #efefef;
}
svg {
width: 54px;
margin-bottom: 20px;
}
JS:
代码语言:javascript复制const landscapeMQ = window.matchMedia("(orientation: landscape)");
const noticeMessage = document.getElementById("notice");
const start = document.getElementById("start");
function handleTabletChange(e) {
// Check if the media query is true
if (e.matches) {
noticeMessage.style.display = "none";
start.style.display = "block";
} else {
noticeMessage.style.display = "block";
start.style.display = "none";
}
}
// Register event listener
landscapeMQ.addListener(handleTabletChange);