Enquire.js 是我创建的一个用于处理 JavaScript 中媒体查询的 JavaScript 库。
“等等!倒回去!JavaScript 中的媒体查询?!这是什么怪胎?”
虽然前提可能看起来很奇怪,但它确实是对响应式工具箱的便捷补充。
概述
Enquire.js 是一个轻量级的纯 JavaScript 库,用于处理媒体查询。它在 GZIP 和压缩后小于 1kb,并且没有任何依赖项。是的,你没看错——没有依赖项,甚至没有 jQuery!如果要支持 没有原生实现的浏览器,则最多需要提供 matchMedia polyfill。
用例
- 响应式设计
- 移动优先设计
- 辅助旧版浏览器使用媒体查询
存在的理由
那么它是如何以及为何产生的呢?我认为最简单的解释方法是通过一个简单的场景……
假设您有一个成熟的网站,目前是固定宽度的,并且针对大屏幕设备。您已经听说过关于“响应式设计”的各种喧嚣,并希望参与其中。因此,您认真地开始设计和规划您的网站如何在小屏幕设备上工作。您已经删除了所有不必要的东西,清除了浮动侧边栏并增加了表单元素的大小。太棒了!
但是那个讨厌的菜单呢?它很庞大,有多个级别,并且依赖于 CSS 悬停效果来显示更深层次的层次结构——换句话说,它在小屏幕触摸设备上无法正常工作!
因此,您在 Google 上搜索了一下,偶然发现了一个将 菜单转换为下拉菜单 的巧妙解决方案。大家击掌庆祝,您的菜单现在可以在移动触摸设备上正常工作了!但是您有一种挥之不去的感觉。您不确定是什么,但有些事情仍然不太对劲。啊,是的,就是这样;即使对于大屏幕设备,您也在运行此代码,即使它们根本不需要它!您为浪费的 CPU 周期流下了眼泪,希望有一种方法可以选择性地运行此 JavaScript。
新的挑战者出现了!
这就是 enquire 介入提供帮助的地方。
现在,我们将继续使用将菜单转换为下拉菜单的场景,在窗口小于 960px 时显示菜单。使用 enquire,它就像这样
enquire.register("max-width: 960px", function() {
// put Chris' code here to convert your menu to a dropdown
});
这仅仅是告诉 enquire 仅当给定的媒体查询匹配时才调用提供的函数。因此,您可以为大屏幕设备节省宝贵的 CPU 周期。好吧,我意识到这有点愚蠢,因为与更大屏幕的设备相比,移动设备通常具有功能较弱的 CPU,但它也从另一个方向起作用——广为人知的移动优先方法!
深入挖掘
在这一点上,在深入探讨一些更具体的场景之前,值得快速浏览一下 enquire API。前面的示例展示了 enquire 最简单的用例——将函数作为第二个参数提供,以便在媒体查询匹配时运行。这在大多数情况下都足够了,但是如果您有更苛刻的要求,enquire 可以做更多的事情。您可以将 register
方法传递一个对象,如下所示
enquire.register("screen and (max-width: 1000px)", {
// REQUIRED
// Triggered when the media query transitions
// from *unmatched* to *matched*
match : function() {},
// OPTIONAL
// Triggered when the media query transitions
// from a *matched* to *unmatched*
unmatch : function() {},
// OPTIONAL
// Triggered once immediately upon registration of handler
setup : function() {},
// OPTIONAL
// Defaults to false
// If true, defers execution of the setup function
// until the first media query is matched (still just once)
deferSetup : true
});
此签名使您可以完全控制,并允许您准确定义您希望发生的事情以及您希望它何时发生。这只有在您调用 enquire 的 listen
函数时才会真正有用(稍后将详细介绍)。
示例
所以让我们来看一些展示更高级功能的示例。
旧版浏览器中的伪媒体查询
Enquire.js 可用于在旧版浏览器中模拟一些基本的媒体查询。但是,请不要将此方法用作 CSS 中真实媒体查询的替代方法,只需将其用作旧版浏览器的修补程序(本示例中使用了 jQuery)
$(function() {
// cache body for speed
var $body = $("body");
// DRY up handler creation
function handlerFactory(className) {
return {
match : function() {
$body.addClass(className);
},
unmatch : function() {
$body.removeClass(className);
}
};
}
// hook up our "media queries"
enquire
.register("screen and (max-width : 320px)", handlerFactory("lt-320"))
.register("screen and (max-width : 640px)", handlerFactory("lt-640"))
.listen();
});
在这里,我们使用 match
和 unmatch
处理程序向 <body>
元素添加或删除类,这使我们能够针对不同尺寸屏幕的样式。请注意,我们可以将对 enquire 所有函数的调用链接起来。
这里尤其值得注意的是对 listen
函数的调用,以便 enquire 将响应浏览器调整大小和方向更改事件(当然,在本示例中,旧版浏览器中方向更改事件不太可能发生)。为了获得最佳性能,listen
会被限制为仅响应给定时间范围内的一个事件。默认情况下,此值为 500 毫秒,但您可以通过将其作为参数传递给 listen 来指定您自己的限制
// 10000 milliseconds = 10 seconds. Why not?!
enquire.listen(10000);
自我测试
让我们来看一个 enquire 在现实世界中的应用示例,该示例取自 enquire.js 项目页面(自我测试 FTW!)。在这里,我想通过默认情况下仅加载最少的 JavaScript 来使页面加载速度尽可能快,选择异步加载任何超出此范围的内容,仅在需要时加载。这样,小屏幕设备(以及代理那些更有可能使用缓慢连接的设备)可以获得最快捷的页面加载速度,只有大屏幕设备需要处理额外的资源。
特别是,此模式用于提取生成和呈现项目页面“跳转到”侧边栏所需的所有代码。此示例已略微简化,以免分散对重要概念的注意力
$(function() {
$toc = $(".toc");
enquire.register("screen and (min-width: 1310px)", [{
deferSetup : true,
setup : function () {
// use Modernizr's yepnope to load
// additional scripts and manage callbacks
Modernizr.load([
{
load: "js/jquery.toc.js",
callback : function() {
// code to generate "Jump To" list
// and append to body
}
},
// load in the bootstrap plugins.
// they hook themselves up via their data-API
"js/vendor/bootstrap-scrollspy.js",
"js/vendor/bootstrap-affix.js"
]);
},
match : function() {
$toc.fadeIn();
},
unmatch : function() {
$toc.hide();
}
}]).listen();
});
这里引入了两个新选项——setup
和 deferSetup
。setup
是一个只调用一次的函数,这非常适合提前完成所有昂贵的 DOM 操作。默认情况下,setup
会在您注册查询处理程序后立即调用,而不管查询是否已匹配。但是,如果您提供 deferSetup
标志,我们可以将设置推迟到第一次匹配查询时。
在这种情况下,它显然是有意义的,因为脚本仅加载到大屏幕设备上。然后,match
和 unmatch
函数用于简单地显示和隐藏“跳转到”列表。
其他示例
以下是一些Enquire可以轻松处理的其他场景。
- 响应式图片
- 使用
data-*
属性,根据匹配的媒体查询切换图片的src
属性。
- 使用
- 重新排列内容
- 有时,仅仅在小屏幕上堆叠元素是不够的,你可能希望内容以完全不同的顺序显示。
- 同样,我建议使用
data-*
属性的声明式方法,这样你就可以选择源元素和目标元素。
更多…
Enquire.js 还有更多功能!你可以在每个查询中注册多个处理程序,以及多个查询,这意味着你拥有终极的灵活性!要了解更多信息,请访问enquire.js 项目页面。
下载、Fork 和贡献!
要下载和获取源代码,请访问github上的项目。如果你希望贡献,请随时这样做,我非常乐意听到大家的意见、想法和创意。尽情地Fork,并提交任何更改的Pull Request。如果你遇到任何问题,请随时在github 上创建 Issue,我会及时解决问题 :)
许可证
该项目采用 MIT 许可证授权,因此您可以随意使用它。
嗯……这段代码很有意思。干得好!!!
感谢你的赞美!非常乐意听到任何反馈。
太棒了!
谢谢!
谢谢,我准备测试一下!
一个问题
我们可以使用其他媒体查询(如设备像素比)还是只能使用max|min宽度?
嗨,作者在这里。我希望它应该可以工作。如果不行,请告诉我(随时在github上提出问题),我会进一步调查。享受吧 :-)
非常棒的工作。我很快就会在某个项目中尝试一下。也很好奇像eQRoeil提到的其他媒体查询。
谢谢!很高兴你找到了它的用途。
请参阅我上面对eQRoeil的回复,它应该可以工作。我希望enquire能够处理所有情况,所以如果你遇到任何问题,请在github上提出问题,我会努力修复。
几天前我还在想是否有什么类似的东西存在。绝对会在我的下一个项目中考虑使用它!
我读懂了你的想法!好吧,也许没有,但很高兴听到它可能满足你的需求!
我之前不知道这个库,非常感谢你提供的非常清晰的示例。使用它与data-*进行图片替换的想法看起来很有前景,我需要深入研究一下这个主题。
这是一个相当新的库,所以你可能没有见过它。很高兴听到这些例子很有帮助!项目网站目前有点简陋,但我将在不久的将来添加更多示例,包括响应式图片 :-)
感谢这篇文章。我之前一直在寻找这样的工具。作为enquire.js的替代方案,我发现了Harvey。它稍微复杂一些,但值得一试。
链接如下:https://github.com/harvesthq/harvey
嗨,basti1350,我确实在某个时候偶然发现了Harvey。正如你所说,Harvey更复杂,而且即使你只想支持具有原生实现的浏览器,也需要使用polyfill。我已经对enquire进行了优化,使其更简单高效,但它仍然拥有Harvey的所有功能,甚至更多!
你说得对,对polyfill的依赖也困扰着我。我今天尝试了enquire,它运行得非常好。
谢谢分享。
这正是我要找的。不过我想知道是否也可以在媒体查询中使用`em`值。
这可能吗?
应该没问题 :-) 但是如果你遇到任何问题,请通过github告诉我。
我从未听说过data-*,并且很好奇想了解更多,有人可以帮我找到一些相关的资源吗?
data-* 属性非常棒!它们作为 html5 的一部分引入,用于存储自定义应用程序样式数据。你可以自定义定义(不会破坏html验证),并根据需要使用它们。将javascript功能声明式地关联起来的好方法。
更多信息:http://html5doctor.com/html5-custom-data-attributes/
啊,听起来很棒!谢谢Nick!
如果你想看一些很棒的用法,可以参考Twitter Bootstrap的javascript小部件,因为它们都有一个“data API”。这已经成为我首选的功能关联方式。
太棒了,我准备在我的当前项目中测试一下 :)
我可能误解了如何将其用作媒体查询更改的监听器?
想象一下:我有一个响应式网页设计,我希望JS重新着色body的背景。
纯CSS的方式是这样的
enquire.js可以实现同样的效果吗?
我不明白的是如何
但这只在页面加载时触发。我认为enquire可以作为一个监听器,并且一旦媒体查询匹配就会始终触发。我是否必须在
$(window).resize(function()
内部运行注册处理程序?想象一个具有不同布局步骤的响应式网页设计(就像大多数响应式网页设计一样),我需要对之前通过JS设置的不同元素应用或删除样式。我目前通过在resize处理程序中查询
window.with
来实现这一点。我可以用enquire.js做到这一点吗?嗨,Matt,
默认情况下,enquire 为了提高效率不会监听 resize 事件。幸运的是,要获得此功能,您只需调用
listen()
。您可以直接对 enquire 进行单独调用,或者将其链接到您的 register 调用中。
这会导致 enquire 监听并响应所有已注册查询的 resize 和 orientation 事件(即不仅仅是一个查询)。
这在文章中,在伪媒体查询示例中进行了讨论。Dogfooding 展示了一个更完整的示例。基本上,您想要使用
match
和unmatch
回调函数。但是,在 CSS 更改状态和 JS 被触发之间存在一点延迟。
如果我像每 10 毫秒一样调用监听器,这会影响性能吗?
如果您没有大量的查询,并且每个查询都没有附加大量的处理程序,那么它应该没问题,但这确实取决于具体情况。如果您有非常复杂或耗费资源的逻辑,您可能会暂时锁死浏览器。我建议您进行一些探索性测试,看看什么适合您。
我也有几个关于如何缓解这个问题的想法,所以请关注 github 项目以获取更新。
干得好……
可能会尝试使用它来处理更新 HTML 属性的声明式方法,如这里所述
http://www.xanthir.com/blog/b4K_0
http://andydavies.me/blog/2012/08/13/what-if-we-could-use-css-to-manipulate-html-attributes/
我今天在 Github 上看到它作为热门项目……查看了一下,想知道它的用途是什么……这篇文章确实帮助我理解了这个简洁的库……并帮助我理解了它的用途……
谢谢……首先是创建这个库……其次是撰写一篇用简单的英语撰写的易于理解的文章……并清楚地展示了一些用例……
哇,直到我看到你的评论,我甚至都没有意识到它在 GitHub 上很受欢迎。真是荣幸!
没问题,我创建它仅仅是一个有趣的实验,并认为也许其他人会发现它有用。很高兴看到我没有错,而且我的写作水平还不错 :-)
太棒了!我一直都在自己做这种事情,用一种即兴的方式。
你知道那种你认为“如果我聪明一点,我会创建一个库以一种可重用的方式抽象出这一点……或者我可以等一两周,其他人会做得更好”的情况吗?
就是这样。
我爱这个社区。
非常感谢你的赞美!希望你以后不用再即兴编写代码了 ;-)
正当我需要的时候。干得好,伙计们。
太棒了!我一直需要这样的解决方案。谢谢!
希望这个库能提供一个更好的响应式设计的图像替换系统;对于动态网站来说,它似乎并不是一个解决方案,因为您基本上需要上传/制作 2-3 个版本的图像。
再说一次,我们谈论的是 JavaScript,而涉及 GD 库的 PHP(RESS,RWD+服务器端组件)解决方案似乎仍然是动态网站的合理选择。
正在寻找适用于 Ruby on Rails 的 RESS 解决方案,但还没有找到;有人知道可以分享的吗?
糟糕,无法编辑;我的意思是,我希望它有一个解决方案可以将“响应式图像”的状态推进到其非常棒的核心功能之外。如果您不想费心处理 CSS 技术细节来实现许多人(如 Trent Walton)在 Codepen 上提供的关于该效果的优秀代码片段,那么内容切换功能非常方便。
enquire 并非旨在处理响应式图像等特定情况,而是足够通用,可以处理大多数涉及媒体查询和 JavaScript 的情况。这是一个经过深思熟虑的决定,旨在保持其体积小巧且轻量级。
我认为,在浏览器中没有本地响应式图像的概念之前,任何解决方案都不能被视为超过解决方法。据我所知,任何关于响应式图像的工作规范都规定了每个分辨率的 URL(这通常意味着多个图像)。因此,使用 enquire 的解决方案可能与它最终的工作方式相差不大。
明白了。我意识到我说话太早了,但无法删除我的原始评论。期待尝试使用 Enquire.js;特别是它允许您创建非常方便的伪媒体查询。
至于修补,我通常先进行 960(~ = 1024)的修补,作为默认值(适用于不支持媒体查询的浏览器),然后向上扩展到 1140(> = 1200px)视图,以及向下扩展到移动设备和纵向平板电脑……这样我就可以避免使用特殊的尺寸类,因为默认值将相当合适。
一个快速的问题,Nick:这与例如 Modernizr 的 `Modernizr.mq('only screen and (max-width: 768px)')` 测试有什么本质区别,该测试会根据窗口的当前状态实时测试给定的媒体查询?
从本质上讲,如果只是在页面加载时进行匹配和不匹配,那么没有太大区别。但 enquire 进一步超越了这一点,允许您监听浏览器事件,*仅*在状态发生变化时调用相应的 match/unmatch 回调函数,因此您无需自己管理它。它还允许您运行一次性设置例程,无论是立即执行还是延迟到查询第一次匹配时执行。最后,它允许您对任何查询进行短路,以便如果您采用移动优先的方法,您仍然可以向旧浏览器(如 IE8)提供桌面体验。
您还可以添加一些行来识别显示屏是否为视网膜显示屏?
哦,谢谢,谢谢!!!
太棒了
谢谢!这正是我需要的!也很容易使用……
太棒了,很高兴听到:) 如果遇到任何问题,请随时在 github 上提出问题。
嗨,Nick
恭喜你的脚本,我真的很喜欢它。
我想你可以通过在“事件超时”上使用不同的访问方式来进一步改进“监听”。我创建的方法反应更快,这是我的建议
我希望它能以某种方式帮助你。
这只是一个建议而已。
继续努力。
来自 Laborator 的 Arlind Nushi
嘿,Arlind,
很高兴你喜欢:) 不幸的是,enquire 没有任何对 jQuery 的依赖,我不希望引入任何依赖。感谢你的帮助。
Nick
非常感谢你分享这个。这正是我今天要找的!在我正在处理的一个网站的移动版本中,重新排序一组 div 的效果非常好。
太棒了!很高兴听到它对你很有用:) 任何功能建议或问题,请随时在 github 上联系我。
简洁易懂!
过去我也自己做过,也用过 modernizr.eq 和自己的计时器一遍又一遍地执行这个函数!
感谢你将这段方便的小代码带给社区!
像你这样的人让互联网变得自由和开放!
继续努力,希望将来能看到你的一些其他酷炫的东西!
有没有办法使用它,而无需在样式表和 JavaScript 文件中重复媒体查询条件?
例如,如果我的 JavaScript 只是使用样式表中定义的尺寸,并且它所做的只是在每次触发不同的媒体查询时重新评估相同的代码(样式表中定义了不同的宽度),那么实际上无需在 JS 中重复 min-width 和 max-with 条件。