在JavaScript中使用媒体查询的教程
大多数现代网站使用响应式网页设计技术来确保它们在任何屏幕大小的设备上的良好外观、可读性和可用性,如手机、平板电脑、笔记本电脑、台式电脑显示器、电视、投影仪等。
使用这些技术的网站都有一个模板,可以根据屏幕大小修改布局:
较小的屏幕通常显示一个线性的单列视图,在这种视图中,通过单击(汉堡包)图标可以激活菜单等UI控件。
更大的屏幕显示更多的信息,可能有水平对齐的侧边栏。UI控件(如菜单项)可能总是可见的,以便于访问。
响应式web设计的一个重要部分就是实现CSS或JavaScript媒体查询,检测设备尺寸,自动提供适合尺寸的设计。我们将讨论为什么这些查询是重要的,以及如何使用它们,但首先,让我们讨论一般的响应式设计。
为什么响应式设计很重要?
响应式设计是如何工作的?
媒体查询最佳实践
媒体查询备选方案
JavaScript中需要媒体查询吗?
选项1:监控字段维度
选项2:定义和监控CSS自定义属性(变量)
选项3:使用匹配的媒体API
为什么响应式设计很重要?
只提供一种页面布局并期望它在任何地方都能工作是不可能的。
当手机在21世纪初第一次获得基本的网络访问时,网站所有者通常会创建两到三个单独的页面模板,大致基于手机和桌面视图。随着设备类型的指数增长,这种做法变得越来越不切实际。
今天,有许多屏幕尺寸,从微型手表显示器到巨大的8K显示器甚至更大。即使只考虑手机,最新设备的分辨率也可以比很多低端笔记本电脑高。
移动设备的使用也比台式电脑多。除非你的网站有特定的用户群,否则你可以期待大多数人通过智能手机访问它。尽管大多数网页设计者、开发者和客户继续使用标准PC,但小屏幕设备不再是马后炮,应该从一开始就考虑。
谷歌已经认识到移动设备的重要性。当一个网站在智能手机上可用并且表现良好时,它在谷歌搜索中的排名会更好。好的内容还是必不可少的,但是加载速度慢,无法适应用户群体屏幕大小的网站可能会危害到你的业务。
最后,考虑可访问性。一个适合所有人的网站,不管他们用什么设备,都会吸引更多的观众。可访问性是许多国家的法律要求,但即使不是你所在的地方,也要考虑更多的观众会带来更多的转化和更高的盈利能力。
响应式设计是如何工作的?
响应式设计基于媒体查询:一种CSS技术,可以根据输出类型(屏幕、打印机甚至语音)、屏幕大小、显示纵横比、设备方向、颜色深度和指针准确度等指标应用样式。媒体也可以考虑用户的偏好,包括减少动画,明暗模式和更高的对比度。
我们的示例演示了仅使用屏幕宽度的媒体查询,但是站点可以更加灵活。有关更多信息,请参见MDN上的全套选项。
媒体查询支持非常出色,已经在浏览器中使用了十多年。仅不支持IE8及以下版本。他们忽略了媒体查询应用程序的风格,但这有时会带来好处(参见下面的最佳实践部分)。
使用媒体查询应用样式有三种标准方法。第一个在HTML代码中加载特定的样式表。例如,当设备的屏幕宽度至少为800像素时,以下标记将加载wide.css样式表:
其次,可以使用@import规则有条件地将样式表加载到CSS文件中:
/* main.css */@import url(‘wide.css’) screen and (min-width: 800px);
请注意,应该避免@import,因为每个导入的CSS文件都是一个渲染块。HTML标签是并行下载的,而@import是串行下载文件。
更常见的是,您将使用@media CSS规则块在样式表中应用媒体查询,这将修改特定的样式。例如:
/* default styles */main { width: 400px;}/* styles applied when screen has a width of at least 800px */@media screen and (min-width: 800px) { main { width: 760px; }}
开发者可以应用任何必要的媒体查询规则来调整站点的布局。
媒体查询最佳实践
在设计最初的媒体查询时,许多网站选择了固定的布局。这在概念上更易于设计和编码,因为它有效地复制了一组有限的页面模板。例如:
小于600像素的屏幕宽度使用400像素宽的移动布局。
600像素和999像素之间的屏幕宽度使用600像素宽的平板电脑布局。
对于大于1000像素的屏幕宽度,请使用1000像素宽的桌面布局。
这项技术有缺陷。在非常小和非常大的屏幕上的结果可能看起来很差,并且随着设备和屏幕大小的改变,可能需要CSS维护。
比较好的选择是使用带断点的“动第一流体”的设计,可以在特定大小下调整布局。本质上,默认布局使用最简单的小屏幕样式在线性垂直块中定位元素。
例如,在容器中:
/* default small-screen device */main { width: 100%;}article, aside { width: 100%; padding: 2em;}
以下是所有浏览器的结果——甚至是不支持媒体查询的非常老的浏览器:
不支持媒体查询的示例屏幕截图。
当支持媒体查询并且屏幕超过特定宽度(例如500px)时,可以水平放置元素。此示例使用CSS网格,其中主要内容使用大约三分之二的宽度,次要内容使用剩余的三分之一:
/* larger device */@media (min-width: 500px) { main { display: grid; grid-template-columns: 2fr 1fr; gap: 2em; } article, aside { width: auto; padding: 0; }}
大屏幕上显示的结果如下:
媒体查询支持的示例屏幕截图
媒体查询备选方案
响应式设计也可以在现代CSS中使用更新的属性来实现,这些属性本质上适应布局,而无需检查视口大小。包括:
Calc、min-width、max-width、min-height、max-height和更新的clamp属性都可以定义尺寸,并根据已知的限制和可用的空调整图元的大小。
视口单元vw、vh、vmin和vmax可以根据屏幕尺寸分数来调整元素的尺寸。
文本可以显示在CSS列中,在空之间允许时可以显示或消失。
您可以使用最小内容、适合内容和最大内容尺寸,根据元素子元素的大小来调整元素的大小。
当元素开始超过可用的空时,CSS box可以换行也可以不换行。
CSS元素可以使用比例分数fr单位来调整大小。重复CSS功能可以与最小最大、自动适应和自动填充相结合,以分配可用的空房间。
新的和(当前的)实验性CSS容器查询可以响应布局中组件可用的parts 空。
这些选项超出了本文的范围,但是它们通常比只能响应屏幕大小的粗糙媒体查询更实用。如果您可以在没有媒体查询的情况下实现布局,它可能会使用更少的代码,效率更高,并且随着时间的推移需要更少的维护。
也就是说,在某些情况下,媒体查询仍然是唯一可行的布局选项。当你需要考虑其他屏幕因素时,它们仍然是必不可少的,比如长宽比、设备方向、颜色深度、指针准确度,或者用户偏好,比如动画和亮/暗模式。
JavaScript中需要媒体查询吗?
到目前为止,我们主要讨论了CSS。这是因为大部分布局问题可以而且应该由CSS单独解决。
但是,在某些情况下,使用JavaScript媒体查询代替CSS是可行的,例如:
菜单等组件在小屏幕和大屏幕上有不同的功能。
切换纵向/横向会影响web应用程序的功能。
基于触摸的游戏必须改变布局或调整控制按钮。
Web应用遵循用户偏好,如暗/亮模式、减少动画、触摸粗糙等。
下一节演示了在JavaScript中使用媒体查询或类似于媒体查询的选项的三种方式。所有示例都返回状态字符串,其中:
小视图=宽度小于400像素的屏幕;
中等视图=宽度在400到799像素之间的屏幕;
大视图= 800像素或更宽的屏幕。
选项1:监控字段维度
在实施媒体问询前的黑暗日子里,这是唯一的选择。JavaScript会监听浏览器“调整大小”事件,使用window.innerWidth和window.innerHeight(或者旧IE中的document.body.clientWidth和document.body.clientHeight)分析视口大小,并做出相应的反应。
此代码将计算出的小型、中型或大型字符串输出到控制台:
const screen = { small: 0, medium: 400, large: 800 };// observe window resizewindow.addEventListener(‘resize’, resizeHandler);// initial callresizeHandler();// calculate sizefunction resizeHandler() { // get window width const iw = window.innerWidth; // determine named size let size = null; for (let s in screen) { if (iw >= screen[s]) size = s; } console.log(size);}
您可以在此查看工作演示。(如果您使用桌面浏览器,请在新窗口中打开此链接,以便于调整大小。移动用户可以旋转设备。)
以上示例在调整浏览器大小时检查视区大小;确定是小、中、大;并将其设置为body元素上的类,从而更改背景色。
这种方法的优点包括:
它可以在任何可以运行JavaScript的浏览器上运行——甚至是古老的应用程序。
您正在捕捉确切的大小,并可以做出相应的反应。
缺点是:
这是一项需要大量代码的老技术。
是不是太精确了?真的需要知道宽度是966px还是967px吗?
您可能需要手动将维度匹配到相应的CSS媒体查询。
用户可以快速调整浏览器大小,使handler函数每次都重新运行。这将通过限制事件使旧的和较慢的浏览器超载。每500毫秒只能触发一次。
总之,除非有非常具体复杂的调整大小需求,否则不要监控视口大小。
选项2:定义和监控CSS自定义属性(变量)
这是一种有点不寻常的技术,它在媒体查询被触发时改变CSS中自定义属性字符串的值。所有现代浏览器(但不是IE)都支持自定义属性。
在以下示例中,@media代码块中的- screen自定义属性设置为“小”、“中”或“大”:
body { –screen: “small”; background-color: #cff; text-align: center;}@media (min-width: 400px) { body { –screen: “medium”; background-color: #fcf; } }@media (min-width: 800px) { body { –screen: “large”; background-color: #ffc; } }
您可以使用伪元素单独在CSS中输出该值(但请注意,它必须用单引号或双引号括起来):
p::before { content: var(–screen);}
您可以使用JavaScript来获取自定义属性值:
const screen = getComputedStyle(window.body) .getPropertyValue(‘–screen’);
但这还不是全部,因为返回值包含了CSS中冒号后定义的所有空 cases和引号字符。字符串将会很“大”,所以需要稍微整理一下:
// returns small, medium, or large in a stringconst screen = getComputedStyle(window.body) .getPropertyValue(‘–screen’) .replace(/W/g, ”);
您可以在此查看工作演示。(如果您使用桌面浏览器,请在新窗口中打开此链接,以便于调整大小。移动用户可以旋转设备。)
本示例每两秒钟检查一次CSS值。它需要一点JavaScript代码,但需要轮询更改—您不能使用CSS来自动检测自定义属性值是否已更改。
也不可能使用DOM variation observer向虚拟元素写入值并检测变化。伪元素不是DOM的“真实”部分!
优势:
这是一个简单的技术,主要使用CSS,匹配真实的媒体查询。您可以同时修改任何其他CSS属性。
不需要复制或解析JavaScript媒体查询字符串。
主要缺点是不能自动响应浏览器视口大小的变化。如果用户将手机从纵向旋转到横向,JavaScript永远不会知道。您可以经常轮询更改,但这是低效的,并且会导致您在演示中看到的时间延迟。
监控CSS自定义属性是一项新颖的技术,但它仅在以下情况下实用:
布局可以固定在页面最初呈现的位置。信息亭或销售点终端是可能的,但是它们可能具有固定的分辨率和单一的布局,因此JavaScript媒体查询变得无关紧要。
该网站或应用经常运行基于时间的功能,例如游戏动画。您还可以检查自定义属性,以确定是否需要更改布局。
选项3:使用matchMedia API
matchMedia API有点不寻常,但是它允许您实现JavaScript媒体查询。IE10以上的大部分浏览器都支持。该构造函数返回一个MediaQueryList对象,对于其特定的媒体查询,该对象的matches属性的值为true或false。
当浏览器视区宽度为800px或更大时,以下代码输出true:
const mqLarge = window.matchMedia( ‘(min-width: 800px)’ );console.log( mqLarge.matches );
“change”事件可应用于MediaQueryList对象。每当matches属性的状态发生变化时,就会触发此操作:它在以前为假(低于800px)后变为真(高于800px),反之亦然。
将MediaQueryList对象作为第一个参数传递给接收处理函数:
const mqLarge = window.matchMedia( ‘(min-width: 800px)’ );mqLarge.addEventListener(‘change’, mqHandler);// media query handler functionfunction mqHandler(e) { console.log( e.matches ? ‘large’ : ‘not large’ ); }
该处理程序仅在matches属性更改时运行。页面初始加载时不会运行,可以直接调用函数来确定启动状态:
// initial statemqHandler(mqLarge);
当您在两种不同的状态之间移动时,API工作得很好。要分析三种或三种以上的状态,比如小、中、大,就需要更多的代码。
首先,用相关的matchMedia对象定义屏幕状态对象:
const screen = { small : null, medium: window.matchMedia( ‘(min-width: 400px)’ ), large : window.matchMedia( ‘(min-width: 800px)’ ) };
不需要在small状态下定义matchMedia对象,因为在small和medium之间移动时会触发medium事件处理程序。
然后,您可以为中型和大型事件设置事件侦听器。它们调用同一个mqHandler()处理函数:
// media query change eventsfor (let [scr, mq] of Object.entries(screen)) { if (mq) mq.addEventListener(‘change’, mqHandler);}
处理函数必须检查所有MediaQueryList对象,以确定它当前是活动的小型、中型还是大型对象。竞争必须按大小排序,因为999px的宽度可以匹配中大型——只有最大的才能“赢”:
// media query handler functionfunction mqHandler() { let size = null; for (let [scr, mq] of Object.entries(screen)) { if (!mq || mq.matches) size = scr; } console.log(size); }
您可以在此查看工作演示。(如果您使用桌面浏览器,请在新窗口中打开此链接,以便于调整大小。移动用户可以旋转设备。)
使用示例如下:
CSS来设置和显示自定义属性(如上面的选项2所示)。
match对象中的matchMedia查询,用于监视JavaScript中的维度更改。JavaScript输出将同时改变。
使用matchMedia API的主要优点是:
它是事件驱动的,可以有效地处理媒体查询更改。
它使用与CSS相同的媒体查询字符串。
缺点是:
处理两个或多个媒体查询需要更多的思考和代码逻辑。
您可能需要在CSS和JavaScript代码中复制媒体查询字符串。如果不保持同步,这可能会导致错误。
为了避免媒体查询不匹配,可以考虑在建筑系统中使用设计令牌。查询字符串在JSON(或类似的)文件中定义,值在构造时插入CSS和JavaScript代码中。
总之,matchMedia API可能是实现JavaScript媒体查询最有效、最实用的方法。它有一些怪癖,但在大多数情况下,它是最好的选择。
总结
CSS大小的选择越来越可行,但是媒体查询仍然是大多数网站响应式网页设计的基础。当处理更复杂的布局和用户偏好时,比如明暗图案,它们总是必要的。
尽可能将媒体查询单独保存到CSS中。当你别无选择只能进入JavaScript的领域时,matchMedia API为JavaScript媒体查询组件提供了额外的控制,这些组件需要额外的基于维度的函数。