Enquire.js – JavaScript 中的媒体查询回调

Avatar of Nick Williams
Nick Williams 发布

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 200 美元的免费额度!

以下是由 Nick Williams 撰写的一篇客座文章。Nick 通过邮件向我介绍了他创建的一个用于辅助响应式设计的新库。具体来说,他正在实现他在 CSS-Tricks 上发现的一个想法,但他希望通过仅在需要时运行脚本的方式来更智能地实现它。于是,这个新的迷你库诞生了!有一些 类似的 库,但这个库非常小,并且与原生媒体查询一起使用。我将让 Nick 详细介绍它……

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();

});

在这里,我们使用 matchunmatch 处理程序向 <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();   

});

这里引入了两个新选项——setupdeferSetupsetup 是一个只调用一次的函数,这非常适合提前完成所有昂贵的 DOM 操作。默认情况下,setup在您注册查询处理程序后立即调用,而不管查询是否已匹配。但是,如果您提供 deferSetup 标志,我们可以将设置推迟到第一次匹配查询时。

在这种情况下,它显然是有意义的,因为脚本仅加载到大屏幕设备上。然后,matchunmatch 函数用于简单地显示和隐藏“跳转到”列表。

其他示例

以下是一些Enquire可以轻松处理的其他场景。

  • 响应式图片
    • 使用data-*属性,根据匹配的媒体查询切换图片的src属性。
  • 重新排列内容
    • 有时,仅仅在小屏幕上堆叠元素是不够的,你可能希望内容以完全不同的顺序显示。
    • 同样,我建议使用data-*属性的声明式方法,这样你就可以选择源元素和目标元素。

更多…

Enquire.js 还有更多功能!你可以在每个查询中注册多个处理程序,以及多个查询,这意味着你拥有终极的灵活性!要了解更多信息,请访问enquire.js 项目页面

下载、Fork 和贡献!

要下载和获取源代码,请访问github上的项目。如果你希望贡献,请随时这样做,我非常乐意听到大家的意见、想法和创意。尽情地Fork,并提交任何更改的Pull Request。如果你遇到任何问题,请随时在github 上创建 Issue,我会及时解决问题 :)

许可证

该项目采用 MIT 许可证授权,因此您可以随意使用它。