以下是 Alessandro Vendruscolo 的客座文章。媒体查询与 CSS 和 JS 相关。在同一位置管理它们的需要和愿望是真实的。一些聪明的做法已经出现,比如 Jeremy Keith 的 条件 CSS。但在这种情况下,您有责任在窗口状态改变后进行测试。您可以通过 MediaQueryList 获取真正的监听器,但这意味着您需要再次在两个地方维护媒体查询。好吧,让 Alessandro 来解释一下吧。
媒体查询最早是在 十二年前(是的,第一份草案可追溯到 2001 年 4 月 4 日!)引入的,旨在限制样式表的范围
媒体查询由媒体类型和一个或多个表达式组成,用于限制特定样式表的范围。[…] 通过使用媒体查询,内容呈现可以针对各种设备进行调整,而无需更改内容本身。
通过使用 CSS 中的媒体查询,我们可以根据例如浏览器屏幕的宽度来选择性地使用样式。
@media screen and (min-width: 960px) {
body {
padding: 50px;
}
}
在上例中,如果您的浏览器屏幕宽度大于或等于 960px,则 body 将具有 50px 的 填充。
从 CSS 动画触发的 DOM 事件
一个简单的点击事件
document.querySelector('a.button').addEventListener('click', function(event) {
// do something
});
这里没什么特别的,只是标准的 DOM 编程。click
事件,以及其他许多事件,是在用户与页面交互时生成的:点击、移动光标、滚动页面等等。
所有这些事件都与 DOM 相关,并且作为用户交互的结果而生成。还有一些与 CSS 动画 专门相关的事件:animationStart
、animationEnd
(及其 过渡 对应事件,transitionStart
、transitionEnd
)。
如果我们向页面中插入一个新元素,并且它有动画,我们知道 CSS 动画会在插入后立即开始。因此,如果我们监听该动画的 animationstart 事件,我们可以知道它何时被插入。
@keyframes nodeInserted {
from { clip: rect(1px, auto, auto, auto); }
to { clip: rect(0px, auto, auto, auto); }
}
.an-element {
animation-duration: 0.001s;
animation-name: nodeInserted;
}
document.addEventListener("animationstart", function (event) {
if (event.animationName == "nodeInserted") {
// an element with class an-element has been inserted in the DOM
}
}, false);
感谢 David Walsh、Daniel Buchner 和 Omar Ismail 大约一年前关于这方面的博文。
将媒体查询和动画结合在一起
也许您能想到这将走向何方。如果我们在媒体查询发生变化时触发动画(而不是在节点插入时),我们也可以在 JavaScript 中检测到媒体查询变化何时发生。
body {
animation-duration: 0.001s;
}
@media screen and (min-width: 1000px) {
body {
animation-name: min-width-1000px;
}
}
@media screen and (min-width: 700px) {
body {
animation-name: min-width-700px;
}
}
@keyframes min-width-700px {
from { clip: rect(1px, auto, auto, auto); }
to { clip: rect(0px, auto, auto, auto); }
}
@keyframes min-width-1000px {
from { clip: rect(1px, auto, auto, auto); }
to { clip: rect(0px, auto, auto, auto); }
}
document.addEventListener(animationEnd, dispatchEvent, false);
// check the animation name and operate accordingly
function dispatchEvent(event) {
if (event.animationName === 'min-width-700px') {
document.body.innerHTML = 'Min width is 700px';
} else if (event.animationName === 'min-width-1000px') {
document.body.innerHTML = 'Min width is 1000px';
}
}
编辑备注:明智地选择您的名称。
我们拥有相同的简单动画,它实际上什么也不做,但这次,动画位于 @media
块内。
如果窗口宽度超过 700px,min-width-700px
动画将触发,我们的 dispatchEvent
将获取此信息。把它想象成一个回调。然后,我们可以通过显示新的部件、销毁其他部件,或执行任何其他操作来适应新的屏幕尺寸。
有趣的是,我们不需要使用窗口大小调整事件,因为该事件可能很慢并且触发次数太多(通常需要节流)。只要媒体查询匹配,animationstart
事件就会触发,即使我们没有调整窗口大小。如果我们随后调整窗口大小,该事件将仅在媒体查询匹配时触发。
正确的方法
现在我们已经讨论了如何在不依赖 resize
事件的情况下获得窗口宽度更改的 JavaScript 回调。
但我建议您不要将此代码用于生产环境。它可以工作,但正确的方法应该是使用 matchMedia
方法,该方法返回一个 MediaQueryList
。MediaQueryList
对象可以拥有监听器,因此我们可以使用 addListener
方法在媒体查询匹配或不匹配时获得通知。
Enquire.js 最近发布了版本 2,它封装了 matchMedia
方法,为我们提供了简洁而强大的包装器。
请问您为什么不建议将此代码用于生产环境…我喜欢 Enquire.js,但我猜它依赖于 MatchMedia,这意味着它可能无法在 IE9 中运行…
IE 10 支持 matchMedia,但这款浏览器在 IE 的发展历程中还算比较新,可能要过一段时间很多 Windows 用户才会升级到 IE 10…
我猜您的解决方案可以在大多数现代浏览器中运行…那么为什么不能在生产环境中使用呢?
这依赖于 CSS 动画,而 IE9 不支持 CSS 动画。另外,MatchMedia 拥有一个 polyfill,因此您仍然可以在较旧的浏览器上使用 enquire.js。
我不会在生产环境中使用它,仅仅为了使用正确的工具来完成工作。这个 hack 可以工作,但它仍然是一个 hack!
它要求您添加一个动画,只是为了检测媒体查询匹配。MatchMedia 是完成此类任务的正确工具。
我是 Enquire 的作者 :)
Alessandro 说得对,您最好使用
matchMedia
,因为它就是为此目的而设计的。如果您为能力较低的浏览器提供 polyfill,就不会有任何问题。Scott Jehl 和 Paul Irish 提供的传统 matchMedia polyfill 使支持 CSS3 媒体查询但不具备 JS
matchMedia
API 的浏览器能够完美地使用matchMedia
。但是,这意味着它只适用于支持 CSS3 媒体查询的浏览器,IE9 和 Opera 12.0 属于此类。如果您需要更多浏览器支持,请考虑使用更深入支持的 polyfill,它将在很大程度上模拟 CSS3 媒体查询。一个很好的例子是 David Knight 的 media-match。它可以追溯到 IE6!当然,这样做会付出代价,因为该 polyfill 的文件大小比之前版本的 polyfill 大得多。
所以,确定浏览器支持,选择合适的 polyfill 来实现目标,然后就可以开始了!
这真是太棒了,但谁能提供一个实际的例子来证明它的实用性,这样我才能证明花几个小时来真正理解它是有意义的?谢谢!
很酷的技巧。我最近发布了 mqa.js(媒体查询别名)来实现这个目的。它与 polyfill 配合良好,因此具有 polyfill 的 IE9 可以完美运行。这个项目的目的是为了维护方便。您不想在 CSS 和 JS 文件中重复相同的媒体查询,mqa.js 通过在媒体查询中使用自定义 ID 选择器来解决这个问题,它会解析该选择器并使用别名在 JavaScript 中触发事件。您可以在我的 GitHub 页面 上了解更多信息,以及一篇 解释该方法的博客文章。
谢谢!
很有见地,好主意。
在 DOM 节点插入技巧出现后,我想过如何使用它,首先想到的就是类更改。
阻止我利用它的原因是 IE9 不支持它。
我以前从未见过 Jeremy Keith 的条件 CSS 文章,但我想到的解决方案,以及我上周 发布的博客文章,基本上是一样的。在我的案例中,我只是对一个隐藏的元素应用颜色,并轻松地确定“断点级别”。通过使用像 #000000、#010101、#020202 这样的十六进制颜色,我的断点分别是 0、1、2 等等。
我之前就想问这个了…… 谢谢你的解释!
是的,我在我的网站 http://pixelgenio.com 上用过几个。我创建 动画视频,但我对 CSS 也保持着浓厚的兴趣。
有一点我不明白。如果它不适合在生产代码中使用,为什么一开始就要推广它呢?
好主意!
嗯,动画事件支持不好。我建议您使用过渡事件,它们的支持度要高得多。使用过渡也更好,因为您不需要创建虚假的动画,您可以重复使用媒体查询中使用的属性进行过渡。