刷新与调整浏览器窗口大小时网格布局呈现不同

Posted

技术标签:

【中文标题】刷新与调整浏览器窗口大小时网格布局呈现不同【英文标题】:Grid layout rendered differently on refresh vs. resizing browser window 【发布时间】:2021-06-08 19:57:41 【问题描述】:

我刚开始使用网格布局,并注意到它在相同的视口大小上呈现不同:

(1) 在最大化窗口中初始显示,这里没什么特别的:

(2) 使用 按钮调整浏览器窗口大小后,左侧边栏中的文本将被换行:

(3) 在不改变大小的情况下刷新浏览器窗口后,去掉左侧边栏中的换行: 注意左侧边栏中的文本突然不再换行,而视口大小仍然相同!

怎么可能?我在 Windows 上使用 Chrome 89.0.4389.82(最新稳定版)。使用 Firefox 86 我也有奇怪的大小调整效果(在小窗口上换行,并且在最大化时换行不会被删除)。想知道两个主要浏览器在简单网格上可能有错误吗?我做错了什么/遗漏了什么?

使用 CSS/html

<style type="text/css">
  body 
    height: 100vh;
    margin: 0;
    display: grid;
    grid-template-rows: auto 1fr;
    grid-template-columns: auto minmax(0, 1fr);
  

  header 
    background-color: #add790;
    text-align: center;
    grid-column: span 2;
  

  nav 
    background-color: orange;
    overflow: auto;
    padding: 1em;
  

  article 
    overflow: auto;
    padding: 1em;
  
</style>

<header>
  <h1>Title</h1>
</header>
<nav>
  <p>Navigation</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
</nav>
<article>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
</article>

更新:添加nav &gt; p min-width: max-content; 规则:

调整窗口大小后: 点击刷新后: 请注意文本和滚动条之间的红色标记间隙。

更新 2: 使用 width: fit-content; 进行导航: 刷新前: 刷新后: 所以导航栏的大小保持不变,但主要内容的位置发生了变化。

【问题讨论】:

我在 Chromium 89 上有同样的错误,但在 Chrome 89 和 Firefox 86(在 linux 上)没有。这只发生在调整大小按钮上,而不是用鼠标调整大小时。所以这可能只是一个错误。 @RomanMkrtchian 谢谢,我也在考虑这是一个错误。只是想知道它存在于 Firefox 和 Chrome 中这样的一个 - 我会说 - 基本示例。让我们看看这里是否至少有人有解决方法的想法。 【参考方案1】:

没什么奇怪的,第一列设置为auto (grid-template-columns: auto ...),所以正如here 所述,它的行为如下:

自动

最大值表示该轨道中项目的最大最大内容大小。 作为最小值表示该轨道中项目的最大最小尺寸(由项目的最小宽度/最小高度指定)。这是 通常,尽管并非总是如此,最小内容大小。 如果在 minmax() 表示法之外使用,则 auto 表示上述最小值和最大值之间的范围。这行为类似 在大多数情况下为 min-content(min-content,max-content)。

css 规则nav &gt; p min-width: max-content; 可以轻松修复“怪癖”

在深入了解细节之前,先说明一下环境:

操作系统: Ubuntu 20.04.2 LTS

Browser-1: Firefox 85.0.1(64 位)

Browser-2: Chromium 版本 89.0.4389.72(官方构建)快照(64 位)

列出所有场景:

1a。初始最大化window

1b。初始最大化iframe

2a。初始最小化window

2b。初始最小化iframe

3a。最大化window

3b。最大化iframe

4a。最小化window

4b。最小化iframe

5 . overflow: scroll(代替overflow: auto,在上述所有场景中)

max-content 与场景相关的行为:

    修复换行 (4b)

    在 (5) 中无效

    火狐

    修复换行(2a2b3a3b4a4b

    在 (5) 中无效

padding 与场景相关的nav 中的问题:

    在 (4b) 中没有右填充

    双右填充(3a, 3b),即 CSS 填充 1em + 滚动条宽度 ~17px

    (5) 中没有问题

    火狐

    在(2a2b3a3b4a4b)中没有右填充

    (5) 中没有问题

可以观察到在场景 (5) 中不存在问题,即当滚动条始终可见时,尽管这种解决方案不是很吸引人。 考虑到上述所有情况,loadresize 之间的不同行为的解释可能如下:

    load,由于窗口的大小是已知的并且还没有渲染任何内容,内容(宽度+填充+滚动+边框)被容纳 根据需要递归多次,直到符合几何形状 最小的性能损失。

    resize 上,当内容已经出现在屏幕上时,随后的布局变化将在帧中呈现以适应新的几何图形(注意 该应用程序调整大小是由操作系统控制的功能,而不是 与window resize) 混淆,所以在调整大小时,当浏览器 引擎检测到溢出,容器nav 可能没有足够的 空间来适应滚动条,虽然在 相邻的容器,以避免回流雪崩(这需要 可观的处理资源和缺乏这些会导致视觉 工件),滚动条的空间取自该上下文。

这意味着单独使用 CSS 解决问题的可能性很小,因为它不提供任何控制,也不会很快在浏览器中修复此行为,所以我们唯一的解决方案是处理这使用javascript。 最明显的解决方案是在window 上监听resize 事件并进一步触发内容重排,对于这里的简单布局,切换overflow: initial 代替overflow: auto 就足够了,更复杂的布局可能需要更激进的样式像width: 0 !important,对于最终控制,resize observer 可以用于特定目标,仅在该上下文中触发回流。

综合这些结论,接下来是解决上述所有场景(在我的环境中*)的解决方法:

    添加 CSS 规则 nav &gt; p min-width: max-content; 添加 CSS 规则 .reflowing * overflow: initial; 在窗口上附加 resize 事件侦听器,这将在 document.body 上切换类 .reflowing 足够长的时间以 生成内容重排,一个requestAnimationFrame (~16ms) 似乎 在这里就够了。
注意:这完全修复了 Chromium,Firefox 仍然存在需要更多 hack 的填充问题,因为它的滚动条似乎只是可滚动元素的覆盖层。

实施:

const initReflow = el => requestAnimationFrame(() => 
    el.classList.add('reflowing');
    requestAnimationFrame(() => el.classList.remove('reflowing'));
);
addEventListener('resize', () => initReflow(document.body));
body 
    background: white;
    height: 100vh;
    margin: 0;
    display: grid;
    grid-template-rows: auto 1fr;
    grid-template-columns: auto minmax(0, 1fr);


header 
    background-color: #add790;
    text-align: center;
    grid-column: span 2;


nav 
    background-color: orange;
    overflow: auto;
    padding: 1em;


article 
    overflow: auto;
    padding: 1em;


nav > p 
    min-width: max-content;

.reflowing * 
    overflow: initial;
<header>
    <h1>Title</h1>
</header>
<nav>
    <p>Navigation</p>
    <p>Some text.</p>
    <p>Some text.</p>
    <p>Some text.</p>
    <p>Some text.</p>
    <p>Some text.</p>
    <p>Some text.</p>
    <p>Some text.</p>
    <p>Some text.</p>
    <p>Some text.</p>
    <p>Some text.</p>
    <p>Some text.</p>
    <p>Some text.</p>
    <p>Some text.</p>
    <p>Some text.</p>
    <p>Some text.</p>
    <p>Some text.</p>
    <p>Some text.</p>
</nav>
<article>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
</article>

资源:

Windowing system

Compositing window manager

Multiple buffering

How browsers work

CSS block formatting context

CSS overflow

免责声明:以上结论并非来自科学或专业材料,而是通用资源,也是个人经验,因此应持保留态度。

【讨论】:

感谢您的回复,但在提到的文档中,我看不到观察到的行为应该正确的原因。我应用了您的修复并对其进行了测试,但它不起作用:刷新仍然会改变布局。请参阅我原来的问题中的编辑): 是的,文档可能看起来很抽象,在我的解释中,auto 是第一个缩小最小化的候选,最后一个增长最大化的候选。如果关于不同的行为,那么很可能是因为刷新布局是从头开始计算的,但仅调整增量的大小,至少在 Chrome 中似乎是这样,但这又过于简单化了,这是关于这个主题的更全面的材料在这里:IHow browsers work【参考方案2】:

我观察到nav 没有提到width,因此它在调整大小和刷新时表现不同。

所以,我所做的是为nav 添加width: fit-content;

nav 
    background-color: orange;
    overflow: auto;
    padding: 1em;
    width: fit-content;
  

这解决了这个问题。您可以查看下面截断的完整 html:

<html><head><style type="text/css">
  body 
    height: 100vh;
    margin: 0;
    display: grid;
    grid-template-rows: auto 1fr;
    grid-template-columns: auto minmax(0, 1fr);
  

  header 
    background-color: #add790;
    text-align: center;
    grid-column: span 2;
  

  nav 
    background-color: orange;
    overflow: auto;
    padding: 1em;
    width: fit-content;
  

  article 
    overflow: auto;
    padding: 1em;
  
</style>

</head><body><header>
  <h1>Title</h1>
</header>
<nav>
  <p>Navigation</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
</nav>
<article>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
</article></body></html>

【讨论】:

感谢您的回复。这确实适用于导航栏,但会导致主要内容权限的新问题。请参阅我的问题中的更新 2。任何想法? :-)【参考方案3】:

问题在于滚动条留下了一个宽度。这很奇怪。我确定这是一个错误

如果您在nav 中设置overflow: overlay 而不是overflow: auto,并使用窗户壁板执行此过程,那么您将看到问题(错误)是overflow: auto

我已经解决了您的问题。我不能说这个解决方案是一个拐杖

此解决方案在于更新&lt;nav&gt; 标签的内容,从而将此标签恢复为其默认宽度

只需将此代码粘贴到您的 js 文件或 &lt;script&gt; 标记中即可使用:

window.onresize = function () 
    let nav = document.querySelector("nav");
    let nav_content = nav.innerHTML;
    nav.innerHTML = nav_content;
;

【讨论】:

这种方法与纯 CSS 重排相比没有任何优势,它具有相同的效果 - 触发重排,但由于整个 DOM 树将被删除并重新创建,因此会对性能产生很大影响,而且, 它不适用于任何类型的现代 Web 应用程序 @syduki, 1. “因为整个 DOM 树将被删除并重新创建” - 什么?? :)) 2. 在你看来,输出的 nav 元素的大小和整个 DOM 树的大小是一样的吗?真奇怪。 3. “它不适合任何现代网络应用程序” - 你是认真的吗,伙计?你很有幽默感:) 1.是的。 2. 是的,nav 元素有它自己的 DOM 树,那棵树中有很多 p 元素,打开 DevTools 看看当你更改 innerHTML 时会发生什么。 3.你能在ReactVueAngular等中使用这个“解决方案”吗?再想想 @syduki,这是更新导航的一种方法。是的,假设我的解决方案是一个拐杖,但它有效。我猜OP不必不断调整窗口大小。在 OP 写的其中一个 cmets 中 - 让我们看看这里是否至少有人对解决方法有想法。我给出了一个解决方案。此外,我已经指出了错误的原因。此外,OP 没有提到他的项目在 js 框架上的工作。请告诉我,您的回答是否有助于解决问题? @s.kuznetsov:仔细研究了您的解决方案。一个我还不知道的非常有趣的技术。非常简单。实现内容以触发浏览器中的重新渲染是一个好主意。确保不要仅在这种情况下使用。谢谢这个建议……这就是我来这里的目的:-)【参考方案4】:

很抱歉,这是一个较长的描述。描述“为什么”并不容易,即使 Chrome 和 Firefox 之间存在不同的行为,并且在浏览器本身中再次出现具有不同行为的不同浏览器事件。

简短:原因

“奇怪”行为的原因是浏览器在网格或表格中使用auto 自动计算列宽的(不同的)方式。但是您看到的是(由浏览器编码人员)预期的浏览器行为。是的:这会让某人发疯...... ;-)

更长:为什么/会发生什么?

一般基础:在您的示例中,导航列的auto width 是根据最宽的元素计算的:“导航”一词。自己检查一下:导航栏的宽度会在您使用文本“Some text more words”使第二个元素更长时发生变化。

Chrome 中的解释行为

在全屏上,窗口的高度足够高,因此导航不需要垂直滚动条。使用窗口按钮使窗口不那么高,因此导航栏中的内容有垂直溢出。浏览器现在添加了垂直滚动条......滚动条发生在导航部分......但由于未知原因,Chrome 没有在该事件中添加自动宽度的重新计算。滚动条只是添加到导航部分。至于这个,现在内容包装的内容的位置更少了。 (注意:“导航”这个词不能换行,现在进入填充。所以它似乎有足够的地方,但事实并非如此。)

当您现在重新加载页面时,它是另一个浏览器事件。您从开始加载页面,浏览器从开始计算自动宽度。在这种情况下,他从一开始就检测到需要滚动条,并且 Chrome 中的自动宽度计算包括从滚动条到导航部分的宽度。

在这种情况下重新加载时,您可以观察到:导航部分变宽了一些。

Firefox 中的解释行为(略有不同)

在 Firefox 中全屏显示与在 Chrome 中相同。足够的导航高度,因此没有垂直滚动条。在 Firefox 中使用窗口按钮缩小窗口与在 Chrome 中几乎相同:将现在需要的垂直滚动条添加到导航中,并且没有明显重新计算自动宽度。但是当 Firefox 识别到导航部分的内容比 auto-width-space 更宽时,似乎仍然需要重新计算。

最大的不同是现在的 reload 事件。与 Chrome 相同:Firefox ('re-') 计算自动宽度。但在 Firefox 中没有任何变化……那是因为 Firefox 总是在没有滚动条的情况下计算自动宽度,并且只将其添加到内部空间容器中……丑陋……但就是这样。

在调整窗口高度时自行检查该行为

如果您使用另一个浏览器事件,您可以确认:只需通过用鼠标跟踪窗口边缘来调整窗口高度。在该事件中,两个浏览器都会重新计算自动宽度。如果新高度使导航栏滚动 Chrome 会添加滚动条并使导航部分更宽。 Firefox 添加了滚动条,但将其放在导航部分的内部,这意味着导航部分的宽度保持不变。

注意:去掉导航部分的几行内容更容易观察效果。

可能的解决方案: 仅 3 处更正

    在导航部分添加 15px 额外填充,以便 Firefox 有足够的空间来放置滚动条。

    将溢出更改为overflow-y: autooverflow-x: hidden 以避免Firefox 中可能出现的水平滚动条

    避免不必要的导航元素换行。在您的情况下,您有内联内容(p 元素中的普通文本)。只需添加nav &gt; p white-space: nowrap 。现在文本不能换行,自动宽度计算采用最宽的文本元素。如果您现在向导航中的某个元素添加更多单词,则可以检查它,即&lt;p&gt;Some text with more words.&lt;/p&gt;

::: 一个很长的解释,但... 仅三个更正 ... 就这些!!!

参见示例。

body 
  height: 100vh;
  margin: 0;
  display: grid;
  grid-template-rows: auto 1fr;
  grid-template-columns: auto minmax(0, 1fr);


header 
  background-color: #add790;
  text-align: center;
  grid-column: span 2;


nav 
  background-color: orange;
  padding: 1em;
  padding-right: calc(1em + 15px);  // added space scrollbar Firefox
  overflow-x: hidden;
  overflow-y: auto;

nav > p 
  white-space: nowrap;


article 
  overflow: auto;
  padding: 1em;
<header>
    <h1>Title</h1>
</header>
<nav>
    <p>Navigation</p>
    <p>Some text with more words.</p>
    <p>Some text.</p>
    <p>Some text.</p>
    <p>Some text.</p>
    <p>Some text.</p>
    <p>Some text.</p>
    <p>Some text.</p>
    <p>Some text.</p>
    <p>Some text.</p>
    <p>Some text.</p>
    <p>Some text.</p>
</nav>
<article>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
    <p>Here too there should be a local scrollbar.</p>
</article>

更新:解决方案描述中的修正。更改了溢出-Y/-X 的错误值。例如,他们是正确的。

更新代码 更正了 cmets 中提到的设置为导航部分(在代码中标记)的填充。

【讨论】:

抱歉坚持:您问的问题与使用自动调整大小(首先在表格上)一样古老,以在 web 早期实现这种布局。现在对于网格布局也是如此。是的:不同浏览器的行为令人困惑。您看到的是处理该类型布局的不同行为的传统(经过测试)标准解决方案。请仔细查看该解决方案:overflow-X 已设置为hidden!这是为了处理布局。与auto-widthwhite-space: no-wrap 一起,效果完全一致:该部分保持打开状态,因此内容部分仅浮动。肯定没有削减! @syduki:请继续回答您最初的问题。 Ubuntu 不是其中的一部分……除了导航区域中询问/显示的行为之外,您没有提到其他额外的填充问题。响应式也不是其中的一部分。传统的处理是/可能是:将导航移到看不见的左边,使导航项更小(即通过使用符号)或使用移动菜单。帮助是可能的。但老实说:所有这些都不是您实际问题的一部分。 哦……那太好了(真的!!!就是这个意思!)……所以我们都是老手,知道时代内 Web 开发的典型挑战和不断变化的任务。如果我将它与 IE @syduki:好的...我收到了您对编码的反馈。但老实说:我无法在这里确认。根据预期的浏览器行为和设置,我在 Windows 10 上的实际浏览器版本(Chrome/Firefox)中没有它......导航部分是否添加了预期的填充来处理 Firefox 行为? ...或者(对不起!)我真的不明白你的意思是什么。 也许我找到了您要求的填充:FF 行为所需的额外填充已设置为导航部分的所有 egdes。我只将其纠正为右边缘。它在代码中标记(calc() 表达式)。 padding right 旨在为右侧自动滚动条提供足够的空间,FF 在导航框中添加内部而不使部分更宽。【参考方案5】:

查看此代码是否符合您的喜好

<style type="text/css">
  body 
    height: 100vh;
    margin: 0;
    display: grid;
    grid-template-rows: auto 1fr;
    grid-template-columns: auto minmax(0, 1fr);
  

  header 
    background-color: #add790;
    text-align: center;
    grid-column: span 2;
  

  nav 
    background-color: orange;
    overflow: auto;
    padding: 1em;
  
  article 
    overflow: auto;
    padding: 1em;
  
  nav::-webkit-scrollbar 
    display: none;
  
  article::-webkit-scrollbar 
    display: none;
  
</style>

<header>
  <h1>Title</h1>
</header>
<nav>
  <p>Navigation</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
  <p>Some text.</p>
</nav>
<article>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
  <p>Here too there should be a local scrollbar.</p>
</article>

【讨论】:

以上是关于刷新与调整浏览器窗口大小时网格布局呈现不同的主要内容,如果未能解决你的问题,请参考以下文章

具有布局适合和网格的 Extjs 3.3.1 FieldSet 在窗口调整大小时不会调整网格大小

在 Qt 表单的不同布局中调整按钮大小

如何在 PyQt5 中保持行和列大小与网格布局相同?

调整网格布局内的标签大小?

Tkinter 网格动态布局

使行扩展元素,即 extjs 网格和图表在窗口调整大小时调整大小