使用 JavaScript 检测动态媒体查询而不在脚本中硬编码断点宽度?

Posted

技术标签:

【中文标题】使用 JavaScript 检测动态媒体查询而不在脚本中硬编码断点宽度?【英文标题】:Detect dynamic media queries with JavaScript without hardcoding the breakpoint widths in the script? 【发布时间】:2017-11-25 02:00:12 【问题描述】:

我一直在寻找一种轻量级、灵活、跨浏览器的解决方案,用于在 javascript 中访问 CSS 媒体查询,不会在 JavaScript 代码中重复 CSS 断点 .

CSS-tricks 发布了一个 CSS3 animations-based solution,这似乎很到位,但它建议使用 Enquire.js 代替。

Enquire.js 似乎仍然需要在脚本中硬编码媒体查询大小,例如

enquire.register("screen and (max-width:45em)",  // do stuff 

问题

到目前为止,所有在 Javascript 中访问媒体查询的解决方案似乎都依赖于在脚本中硬编码的断点。如何以一种只允许在 CSS 中定义的方式访问断点,而不依赖于.on('resize')

尝试的解决方案

我已经制作了自己的版本,可以在 IE9+ 中运行,使用隐藏元素,该元素使用 :content 属性在 Query 触发时添加我想要的任何内容(与 ZeroSixThree's solution 相同的起点):

html

<body>
    <p>Page content</p>
    <span id="mobile-test"></span>
</body>

CSS

#mobile-test 
    display:none;
    content: 'mq-small';

@media screen only and (min-width: 25em) 
    #mobile-test 
        content: 'mq-medium';
    

@media screen only and (min-width: 40em) 
    #mobile-test 
        content: 'mq-large';
    

使用 jQuery 的 JavaScript

// Allow resizing to be assessed only after a delay, to avoid constant firing on resize. 
var resize;
window.onresize = function() 
    clearTimeout(resize);
    // Call 'onResize' function after a set delay
    resize = setTimeout(detectMediaQuery, 100);
;

// Collect the value of the 'content' property as a string, stripping the quotation marks
function detectMediaQuery() 
    return $('#mobile-test').css('content').replace(/"/g, '');


// Finally, use the function to detect the current media query, irrespective of it's breakpoint value
$(window).on('resize load', function() 
    if (detectMediaQuery() === 'mq-small') 
        // Do stuff for small screens etc
    
);

这样,媒体查询的断点完全由 CSS 处理。如果您更改断点,则无需更新脚本。如何做到这一点?

【问题讨论】:

window.onresize 处理函数的目的是什么?它似乎将您的detectMediaQuery 函数消除了 100 毫秒,但该函数除了返回一个字符串之外什么也不做。处理程序甚至不使用它。 肯定不理想,那段代码是从this question挪用的 我并不是说它不理想,我是说它看起来完全是死代码,混淆了你的问题。 我的意思是我的代码并不理想 :) 但感谢您提供的信息 【参考方案1】:

我设法通过为不可见元素创建宽度规则来获取断点值。

HTML:

<div class="secret-media-breakpoints">
    <span class="xs"></span>
    <span class="tiny"></span>
    <span class="sm"></span>
    <span class="md"></span>
    <span class="lg"></span>
    <span class="xl"></span>
</div>

CSS:

$grid-breakpoints: (
    xs:   0,
    tiny: 366px,
    sm:   576px,
    md:   768px,
    lg:   992px,
    xl:   1200px
);

.secret-media-breakpoints 
    display: none;

    @each $break, $value in $grid-breakpoints 
        .#$break 
            width: $value;
        
    

JavaScript:

app.breakpoints = ;
$('.secret-media-breakpoints').children().each((index, item) => 
    app.breakpoints[item.className] = $(item).css('width');
);

【讨论】:

【参考方案2】:

请参阅来自专家 David Walsh Device State Detection with CSS Media Queries and JavaScript 的这篇帖子:

CSS

.state-indicator 
    position: absolute;
    top: -999em;
    left: -999em;

.state-indicator:before  content: 'desktop'; 

/* small desktop */
@media all and (max-width: 1200px) 
    .state-indicator:before  content: 'small-desktop'; 


/* tablet */
@media all and (max-width: 1024px) 
    .state-indicator:before  content: 'tablet'; 


/* mobile phone */
@media all and (max-width: 768px) 
    .state-indicator:before  content: 'mobile'; 

JS

var state = window.getComputedStyle(
    document.querySelector('.state-indicator'), ':before'
).getPropertyValue('content')

此外,这是来自 javascript 大师 Nicholas C. Zakas 的巧妙解决方案:

  // Test a media query.
  // Example: if (isMedia("screen and (max-width:800px)")
  // Copyright 2011 Nicholas C. Zakas. All rights reserved.
  // Licensed under BSD License.
  var isMedia = (function () 

    var div;

    return function (query) 

      //if the <div> doesn't exist, create it and make sure it's hidden
      if (!div) 
        div = document.createElement("div");
        div.id = "ncz1";
        div.style.cssText = "position:absolute;top:-1000px";
        document.body.insertBefore(div, document.body.firstChild);
      

      div.innerHTML = "_<style media=\"" + query + "\"> #ncz1  width: 1px; </style>";
      div.removeChild(div.firstChild);
      return div.offsetWidth == 1;
    ;
  )();

【讨论】:

谢谢,*** JS 解决方案看起来很漂亮,但它仍然依赖于对查询断点进行硬编码。第二个解决方案看起来不错,但是 David Walsh 的解决方案通过使用 z-index 而不是 content 解决了浏览器支持问题,然后通过 JS 对象将每个 z-index 值重新分配给所需的文本值 如果您不想将 .state-indicator 元素添加到 DOM,您似乎也可以使用 body:before(至少在 Chrome 中)。 这些都是巧妙的变通办法,但难道没有一种“官方”的方式可以知道哪些媒体查询在 CSS 中处于活动状态吗?【参考方案3】:

我找到了一个 hackish 但简单的解决方案:

@media (min-width: 800px) 
.myClass
    transition-property: customNS-myProp;

这个 css 属性只是一个标记,可以在 JS 中知道是否达到了断点。根据规范,transition-property 可以包含任何内容并受 IE 支持(请参阅https://developer.mozilla.org/en-US/docs/Web/CSS/transition-property 和 https://developer.mozilla.org/en-US/docs/Web/CSS/custom-ident)。

如果transition-property 有值,则只需检查js。以 TS 中的 JQuery 为例:

const elements: JQuery= $( ".myClass" );
        $.each( elements, function (index, element) 
            const $element = $( element );
            const transition = $element.css( "transition-property" );
            if (transition == "custNS-myProp") 
                // handling                     ...
            
        );

当然,在 wiki 中有一个警告词,即 css 属性标识符的域正在发展,但我想如果你为值加上前缀(例如这里使用 customNS),你可以避免冲突。

将来,当 IE 支持它们时,使用自定义属性而不是 transition-property https://developer.mozilla.org/en-US/docs/Web/CSS/--*.

【讨论】:

【参考方案4】:

试试这个

const mq = window.matchMedia( "(min-width: 500px)" );

matches 属性根据查询结果返回 true 或 false,例如

if (mq.matches) 

  // window width is at least 500px
 else 
  // window width is less than 500px

您还可以添加一个在检测到更改时触发的事件侦听器:

// media query event handler
if (matchMedia) 
  const mq = window.matchMedia("(min-width: 500px)");
  mq.addListener(WidthChange);
  WidthChange(mq);


// media query change
function WidthChange(mq) 
  if (mq.matches) 
    // window width is at least 500px
   else 
    // window width is less than 500px
  


【讨论】:

感谢@Taioli,但这仍然涉及将断点值硬编码到 JavaScript 中。我试图找出如何实现这个结果没有硬编码断点值 多年后,我仍然不明白为什么当它错过了问题的要点时,为什么会被赞成——而不是在 JavaScript 中硬编码断点值。 仍然被赞成,仍然错过了这个问题。所以在工作;p

以上是关于使用 JavaScript 检测动态媒体查询而不在脚本中硬编码断点宽度?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 CSS 媒体查询检测设备方向?

CSS媒体查询不足以检测超大型移动设备/平板电脑,而不是台式机[重复]

我需要一种使用 jquery 检测 CSS3 媒体查询支持的简单方法

如何使用 Javascript 或 CSS 在 DIV 上应用媒体查询 [重复]

为移动设备、低分辨率桌面和高分辨率桌面编写媒体查询 [重复]

是否可以使用 sass 函数在媒体查询中从父宽度动态获取子高度? [复制]