在 JavaScript 中使用媒体查询的全面指南

发布时间:2021-12-06 15:33:26 阅读:337

大多数现代网站使用响应式网页设计技术,以确保它们看起来很好,具有可读性,并且在任何屏幕尺寸的设备上都可以使用,比如手机、平板电脑、笔记本电脑、台式 PC 显示器、电视、投影仪等等。

使用这些技术的网站有一个单一的模板,它修改布局响应屏幕尺寸:

  • 较小的屏幕通常显示线性的单列视图,其中通过单击 (汉堡包) 图标激活菜单等 UI 控件。
  • 更大的屏幕显示更多的信息,可能会有水平对齐的边栏。UI 控件 (如菜单项) 可能总是可见的,以便更容易访问。

响应式网页设计的一个重要部分是实现 CSS 或 JavaScript 媒体查询,以检测设备大小,并自动为该大小提供适当的设计。我们将讨论为什么这些查询很重要以及如何使用它们,但首先,让我们从总体上讨论响应式设计。

为什么响应式设计很重要?

提供单一的页面布局并期望它在任何地方都能工作是不可能的。

当移动电话在 21 世纪初首次获得基本的 web 访问时,网站所有者通常会创建两到三个独立的页面模板,大致基于移动和桌面视图。随着设备种类呈指数级增长,这变得越来越不切实际。

今天,有许多屏幕尺寸,从小型手表显示器到巨大的 8k 显示器,甚至更多。即使你只考虑移动电话,最近的设备也可以比许多低端笔记本电脑拥有更高的分辨率。

移动设备的使用也超过了台式电脑。除非你的站点有一组特定的用户,否则大多数人都可以通过智能手机访问站点。小屏幕设备不再是事后的想法,应该从一开始就考虑,尽管大多数网页设计师、开发人员和客户仍在使用标准 PC。

谷歌已经认识到移动设备的重要性。当网站可用并且在智能手机上表现良好时,它们在谷歌搜索中排名更高。好的内容仍然是至关重要的,但是一个加载缓慢的站点如果不能适应你的用户群的屏幕尺寸,可能会损害你的业务。

最后,考虑可访问性。一个适合所有人的网站,无论他们使用什么设备,都会有更多的用户。在许多国家,可访问性是一项法律要求,但即使不是在你所在的国家,你也要考虑到,更多的观众将带来更多的转换率和更高的盈利能力。

响应式设计是如何工作的?

响应式设计的基础是媒体查询:一种 CSS 技术,可以根据诸如输出类型 (屏幕、打印机甚至语音)、屏幕尺寸、显示长宽比、设备方向、颜色深度和指针精度等指标应用样式。媒体查询也可以考虑用户的偏好,包括减少动画,亮 / 暗模式,和更高的对比度。

我们所展示的示例只使用屏幕宽度来演示媒体查询,但是站点可以更加灵活。有关详细信息,请参阅 MDN 上的全部选项集。

媒体查询支持非常出色,并且已经在浏览器中存在了十多年。只有 IE8 及以下版本不支持。它们忽略了媒体查询应用的样式,但这有时是有好处的 (请参阅下面的最佳实践部分)。

有三种使用媒体查询应用样式的标准方法。第一种方法在 HTML 代码中加载特定的样式表。例如,当设备的屏幕宽度至少为 800 像素时,下面的标签加载 width .css 样式表:

<link rel="stylesheet" media="screen and (min-width: 800px)" href="wide.css" /> 

其次,样式表可以使用 @import at-rule 有条件地加载到 CSS 文件中:

/* main.css */
@import url('wide.css') screen and (min-width: 800px); 

注意,@import 应该避免使用,因为每个导入的 CSS 文件都是渲染阻塞的。HTML <link>标签是并行下载的,而 @import 是串行下载文件的。

更典型的是,你将使用修改特定样式的 @media CSS at-rule 块在样式表中应用媒体查询。例如:

/* 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;
  }
} 

开发人员可以应用任何必要的媒体查询规则来适应站点的布局。

媒体查询最佳实践

当媒体查询最初设计出来时,许多网站选择了一套严格固定的布局。这在概念上更容易设计和编写代码,因为它有效地复制了有限的一组页面模板。例如:

  • 小于 600px 的屏幕使用 400px 的移动布局。
  • 屏幕宽度在 600px 和 999px 之间,使用 600px 宽的平板布局。
  • 大于 1000px 的屏幕宽度使用 1000px 的桌面布局。
  • 这项技术存在缺陷。在非常小和非常大的屏幕上,结果可能看起来很差,并且由于设备和屏幕大小随时间变化,需要进行 CSS 维护。

一个更好的选择是使用移动优先流体设计,带有断点,以适应特定大小的布局。本质上,默认布局使用最简单的小屏幕样式,将元素放在垂直的线性块中。

例如:<article><aside><main>容器中:

/* default small-screen device */
main {
  width: 100%;
}

article, aside {
  width: 100%;
  padding: 2em;
} 

当支持媒体查询并且屏幕超过特定宽度时,比如 500px,<article><aside>元素可以水平放置。这个例子使用了一个 CSS 网格,主要内容使用大约三分之二的宽度,次要内容使用剩余的三分之一:

/* larger device */
@media (min-width: 500px) {
  main {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: 2em;
  }

  article, aside {
    width: auto;
    padding: 0;
  }
} 

媒体查询选择

响应式设计也可以在现代 CSS 中使用更新的属性来实现,这些属性本质上适应布局,而无需检查视口尺寸。选项包括:

  • calcmin-widthmax-widthmin-heightmax-height 和更新的 clamp 属性都可以根据已知的限制和可用空间来定义元素的尺寸。
  • 视口单元 vwvhvminvmax 可以根据屏幕尺寸分数来确定元素的大小。
  • 文本可以显示在 CSS 列,出现或消失的空间允许。
  • 可以使用 min-contentfit-contentmax-content 维度来根据子元素的大小调整元素的大小。
  • 当元素开始超过可用空间时,CSS flexbox 可以包装或不包装元素。
  • CSS 网格元素可以按比例比例缩放。repeat CSS 函数可以与 minmaxauto-fitauto-fill 一起使用,以分配可用空间。
  • 新的 (目前) 实验性的 CSS 容器查询可以对布局中组件可用的部分空间作出反应。

这些选项超出了本文的范围,但它们通常比只响应屏幕尺寸的粗糙媒体查询更实用。如果你可以在没有媒体查询的情况下实现布局,它可能会使用更少的代码,更高效,并且需要更少的维护。

也就是说,在有些情况下,媒体查询仍然是唯一可行的布局选择。当你需要考虑其他屏幕因素,如长宽比、设备方向、颜色深度、指针精度或用户偏好 (如减少动画和明暗模式) 时,它们仍然是必不可少的。

你需要 JavaScript 中的媒体查询吗?

到目前为止,我们主要讨论的是 CSS。这是因为大多数布局问题都可以 —— 也应该 —— 在 CSS 中单独解决。

然而,在某些情况下,使用 JavaScript 媒体查询而不是 CSS 是可行的,例如:

  • 组件 (如菜单) 在大小屏幕上具有不同的功能。
  • 从纵向 / 横向切换会影响 web 应用的功能。
  • 触屏游戏必须改变画布布局或调整控制按钮。
  • 网页应用遵循用户偏好,如暗 / 亮模式、简化动画、触摸粗糙度等。

下面几节演示了在 JavaScript 中使用媒体查询 (或类似媒体查询的选项) 的三种方法。所有的例子都返回一个状态字符串:

  • 小视图 = 宽度低于 400 像素的屏幕;
  • 中等视图 = 宽度在 400 到 799 像素之间的屏幕;
  • 大视图 = 宽度为 800 像素或更多的屏幕。

选项 1: 监视视口尺寸

在媒体查询实现之前的黑暗日子里,这是唯一的选择。JavaScript 会监听浏览器的 “resize” 事件,分析 viewport 维度使用 window.innerWidthwindow.innerHeight (或旧 IEs 中的 document.body.clientWidthdocument.body.clientHeight),并相应地做出反应。

这段代码将计算出的小、中或大字符串输出到控制台:

const
  screen = {
    small: 0,
    medium: 400,
    large: 800
  };

// observe window resize
window.addEventListener('resize', resizeHandler);

// initial call
resizeHandler();

// calculate size
function 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 媒体查询。
  • 用户可以快速调整浏览器的大小,使处理程序函数每次都重新运行。这可能会通过限制事件来重载较旧和较慢的浏览器。它只能每 500 毫秒触发一次。

总之,除非你有非常具体和复杂的大小要求,否则不要监视视口尺寸。

选项 2: 定义和监视 CSS 自定义属性 (变量)

这是一种稍微不同寻常的技术,它在触发媒体查询时改变 CSS 中自定义属性字符串的值。所有现代浏览器都支持自定义属性 (IE 除外)。

在下面的例子中,在 @media 代码块中 ——screen 自定义属性被设置为 "small", "medium" 或 "large":

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 中冒号之后定义的所有空格和引号字符。字符串将是 ' "large" ',因此需要进行一些整理:

// returns small, medium, or large in a string
const screen = getComputedStyle(window.body)
                 .getPropertyValue('--screen')
                 .replace(/\W/g, ''); 

你可以在这里查看一个工作演示。(如果使用桌面浏览器,请在新窗口中打开此链接,以便更容易调整大小。移动用户可以旋转设备。)

该示例每两秒检查一次 CSS 值。它需要一些 JavaScript 代码,但是有必要对更改进行轮询 — 你不能使用 CSS 自动检测自定义属性值的更改。

也不可能将值写入伪元素并使用 DOM 突变观察者检测变化。伪元素不是 DOM 的 “真正” 部分!

选项 3: 使用 matchMedia API

matchMedia API 有点不寻常,但它允许你实现一个 JavaScript 媒体查询。大多数 IE10 以上的浏览器都支持它。构造函数返回一个 MediaQueryList 对象,该对象具有一个 match 属性,对于其特定的媒体查询,该属性的计算结果为 true 或 false。

以下代码在浏览器视口宽度为 800px 或更大时输出 true

const mqLarge  = window.matchMedia( '(min-width: 800px)' );
console.log( mqLarge.matches ); 

可以将 “更改” 事件应用于 MediaQueryList 对象。每当 matches 属性的状态发生变化时,就会触发此操作:它在之前为 false (低于 800px) 的情况下变成 true (超过 800px),反之亦然。

接收处理函数传递 MediaQueryList 对象作为第一个参数:

const mqLarge  = window.matchMedia( '(min-width: 800px)' );
mqLarge.addEventListener('change', mqHandler);

// media query handler function
function mqHandler(e) {
 
  console.log(
    e.matches ? 'large' : 'not large'
  );
 
} 

处理程序只在匹配属性更改时运行。它不会在页面初始加载时运行,所以你可以直接调用该函数来确定启动状态:

// initial state
mqHandler(mqLarge); 

当你在两个不同的状态之间移动时,API 工作得很好。要分析三种或更多的状态,如小型、中型和大型,需要更多的代码。

首先定义一个带有关联 matchMedia 对象的屏幕状态对象:

const
  screen = {
    small : null,
    medium: window.matchMedia( '(min-width: 400px)' ),
    large : window.matchMedia( '(min-width: 800px)' )
  }; 

没有必要在小状态上定义 matchMedia 对象,因为当在 smallsmall 之间移动时,medium 事件处理程序将触发。

然后可以为 mediumlarge 事件设置事件侦听器。它们调用相同的 mqHandler () 处理函数:

// media query change events
for (let [scr, mq] of Object.entries(screen)) {
  if (mq) mq.addEventListener('change', mqHandler);
} 

处理程序函数必须检查所有 MediaQueryList 对象,以确定当前是smallmedium的还是large。匹配必须按大小顺序运行,因为 999px 的宽度将匹配mediumlarge - 只有large的应该 “获胜”:

// media query handler function
function mqHandler() {
 
  let size = null;
  for (let [scr, mq] of Object.entries(screen)) {
    if (!mq || mq.matches) size = scr;
  }
 
  console.log(size);
 
} 

你可以在这里查看一个工作演示。(如果使用桌面浏览器,请在新窗口中打开此链接,以便更容易调整大小。移动用户可以旋转设备。)

示例使用如下:

  1. 在 CSS 中设置和显示自定义属性的媒体查询 (如上面的选项 2 所示)。
  2. 在 matchMedia 对象中使用相同的媒体查询来监控 JavaScript 中的维度变化。JavaScript 输出将在同一时间发生改变。

使用 matchMediaAPI 的主要优点是:

  • 它是事件驱动的,在处理媒体查询更改时效率很高。
  • 它使用与 CSS 相同的媒体查询字符串。

缺点:

  • 处理两个或多个媒体查询需要更多的思考和代码逻辑。
  • 你可能需要在 CSS 和 JavaScript 代码中复制媒体查询字符串。如果不保持它们同步,可能会导致错误。

为了避免媒体查询不匹配,你可以考虑在构建系统中使用设计标记。媒体查询字符串在 JSON (或类似的) 文件中定义,并在构建时将值插入 CSS 和 JavaScript 代码中。

总结

总之,matchMedia API 可能是实现 JavaScript 媒体查询的最有效和实用的方法。它有一些怪癖,但它是大多数情况下的最佳选择。

固有的 CSS 大小选择越来越可行,但媒体查询仍然是大多数网站响应式网页设计的基础。他们总是需要处理更复杂的布局和用户偏好,如亮 / 暗模式。

在可能的情况下,尽量只对 CSS 进行媒体查询。当你不得不冒险进入 JavaScript 领域时,matchMedia API 为 JavaScript 媒体查询组件提供了额外的控制,这些组件需要额外的基于维度的功能。


我们在微信上24小时期待你的声音
解答:网站优化,网站建设,搜索引擎优化,APP 开发,小程序开发

非常感谢您有耐心的读完这篇文章:"在 JavaScript 中使用媒体查询的全面指南",此文章仅为提供更多信息供用户参考使用或为学习交流的方便。如果对您有帮助,请收藏我们的网址:https://www.91webs.cn


18617670560