如何避免媒体查询重叠?

14
级联是CSS的特殊和强大之处。但在媒体查询中,重叠可能会出现问题。
考虑以下CSS(继续CSS媒体查询重叠规则):
/* Standard - for all screens below 20em */

body { color: black; font-size: 1em; }

/* Query A - slightly wider, mobile viewport */

@media (min-width: 20em) and (max-width: 45em) {
    body { color: red; } /* supposed to be unique for this width */
}

/* Query B - everything else */

@media (min-width: 45em) {
    body { font-size: larger; } /* because viewport is bigger */
}

当屏幕宽度恰好为45em时,根据标准CSS级联规则,45em处的重叠部分将被处理
  • 首先应用所有 max-width: 45em 定义,
  • 然后应用所有 min-width: 45em 定义。
考虑以下两种情况:
  • 所有文本通常为黑色,但查询A是唯一的,具有color: red
  • 由于查询B适用于较大的视口,因此其文本具有CSS font-size: larger
因此,在恰好为45em的宽度下,我们会得到大而红的文本避免这种情况的最佳解决方案是什么?
我看到两种可能性:
  1. 查询B中重新声明文本为color: black,但如果将来选择更改color,则需要管理两个声明。(当然,这一行代码没有这样的问题,但是想象一下有很多其他声明和选择器。)

  2. 通过使用像max-width: 799pxmin-width: 800px这样的像素值来避免重叠,但那么你就使用了像素——我猜它们分别可以是49.9375em和50em。虽然如果默认值不再是16em并且某些值被四舍五入会怎样呢?而且我们仍然不确定在那个间隙发生了什么。(一个打破时空连续性的黑洞?)

两者都有缺点...还有其他想法吗?


1
你过度分析了:你永远不会编写那样的样式。媒体查询通常用于调整特定视口中的元素。没有理由选择像49.9375em这样的断点,而不是49.99999em。 - cimmanon
@cimmanon,我是把那个作为一个次要点来说的(可以忽略奇怪的“em” 值,只是为了保持问题一致 —— 我考虑过用“px” 来写整个问题,但我更喜欢“em”)。 - Baumr
@Baumr--我认为cimmanon的意思是你应该使用你的第二个解决方案,但在max-width上将值设置为49.99999em,这样你就不必处理“间隙”(就像你可能会遇到49.9375em一样)。 - ScottS
1
浏览器在计算像素值时会四舍五入,因此我理解,在 50em 时仍然会应用两者。 - Baumr
2个回答

13

对于任何媒体查询,创建两个互相排斥的@media块的唯一可靠方法是在其中一个块中使用not进行否定。不幸的是,这意味着每个@media块需要重复您的媒体查询。因此,例如,不要使用以下方法:

@media (max-width: 49.9375em) {
    body {
        color: red;
    }
}

@media (min-width: 50em) {
    body {
        font-size: larger;
    }
}

你将会得到这个:

/* 
 * Note: Media Queries 4 still requires 'not' to be followed by a
 * media type (e.g. 'all' or 'screen') for reasons I cannot comprehend.
 */
@media not all and (min-width: 50em) {
    body {
        color: red;
    }
}

@media (min-width: 50em) {
    body {
        font-size: larger;
    }
}

交互式的 jsFiddle 演示

这种方法在关闭范围媒体特性,比如 widthheight, 上非常有效,因为它本质上将此转化为一个二选一的情况。但是,与你前两个选项一样,它并不完美:正如提到的,你必须重复相同的媒体查询两次,并在其中添加 not。就像条件规则 3所描述的那样,@media 中没有 if/else 结构。


虽然我在回答你之前的问题中提到了这一点:

从我的实验来看,iOS 上的 Safari 会四舍五入所有小数像素值,以确保 max-width: 799pxmin-width: 800px 中的任意一个都能匹配,即使视口实际上是 799.5px(显然匹配前者)。

仍然应该注意到,当涉及到四舍五入时,我注意到了一些怪异的现象。尽管如此,我还没有找到一个小数值可以规避 两个 媒体查询并最终不接受任何一组规则的样式(顺便说一句,这是最糟糕的情况,所以不要担心可能会创建时空裂缝)。这必须意味着浏览器——至少在我测试过的 Safari 中——做了合理的工作,以确保它们满足媒体查询,即使你有不同的值(精确到1个 CSS 像素)。

然而,当涉及到在桌面浏览器上可以观察到较大差距的单位,比如 ems 时,误差就会更大。例如,有一个评论建议使用 49.99999em 而不是 49.9375em 这样更随意的值,但显然存在差异,至少在默认字体大小为 16px 的情况下。

我简化了你的代码,将媒体查询更改为使用小数值,并将代码放在了 jsFiddle 上:

@media (max-width: 49.9375em) {
    body {
        color: red;
    }
}

@media (min-width: 50em) {
    body {
        font-size: larger;
    }
}

如果您将结果窗格调整为恰好800像素(文本将更新以指导您),实际上会出现不同的结果,具体取决于是使用@media(max-width: 49.9375em)还是@media(max-width: 49.99999em)(我也感到惊讶)...

无论如何,您是正确的:选项2也有其缺点。 老实说,我并不特别喜欢它,因为我不想为超出我的控制范围的设备和用户代理怪癖而大费周折。如果您和我一样,我想重新声明规则会更好,虽然这可能会带来一些不便,但至少作为作者,仍然在您的控制范围内。


1
@Baumr:你提出的 below-widthabove-width 给了我一个想法,但我不确定它是否与你的确切要求相符。等我编辑完后再看一下。 - BoltClock
这个话题有什么新消息吗?关于缩放,浏览器是否已经开始将px和em媒体查询视为同等对待?有没有找到重叠问题的解决方案?谢谢! - dalgard
1
@ADTC:您可以嵌套@media规则,或使用<link/style media="screen">来解决媒体类型限制的问题。例如,@media screen { @media not (min-width: 50em) {...} },或者<style media="screen">@media not (min-width: 50em) {...}</style>。我不记得上次更新答案时是否完全支持@media嵌套,但我知道我在2018年更新了另一个答案,其中包含此信息 - BoltClock
1
@ADTC:我几年前就停止了作为开发人员的活动,更不用说在Stack上了,但我很高兴我的大部分CSS知识仍然相关,并且我仍然可以教授新的东西给人们 :) - BoltClock
显示剩余8条评论

1
对我来说,最好的方法是保留0.01em的间隔:

@media (min-width: 20em) and (max-width: 44.99em) {
    body { color: red; } /* supposed to be unique for this width */
}
@media (min-width: 45em) {
    body { font-size: larger; } /* because viewport is bigger */
}

我建议您阅读本文,了解不同的解决方案以避免媒体查询重叠的详细信息和比较。
谢谢, 托马斯。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接