【问题标题】:Why does a grid-area name absent in grid-template-areas create additional tracks?为什么网格模板区域中缺少网格区域名称会创建额外的轨道?
【发布时间】:2019-12-23 04:22:51
【问题描述】:

我创建了一个简单的 CSS 网格,我决定不指定 grid-templategrid-template-columnsgrid-template-rows 属性。

相反,我从grid-template-areas 开始,并通过grid-area 属性将区域名称分配给grid-items

在那之后,我对如果从 grid-template-areas 中删除 grid-item 会发生什么很感兴趣。结果有点奇怪。

移除的grid-item被放置在右侧,并由附加列分隔。

问题:

为什么会这样?这是预期的行为还是我错过了代码中的某些内容?如何删除此列?

body {
  display: grid;
  grid-template-areas: 
     "header"
     "footer";
}

header {
  grid-area: header;
  background: lightblue;
}

main {
  grid-area: main;
  background: darkorange;
}

footer {
  grid-area: footer;
  background: blue;
}
<header>Header</header>
<main>Main</main>
<footer>Footer</footer>

【问题讨论】:

  • 为@TemaniAfif 发布赏金,以获得经过充分研究和详细的答案。欢迎提供其他答案,并将考虑作为赏金的一部分。
  • @Michael_B 谢谢 :) 但你也做了很大一部分工作来寻找罪魁祸首,这就是我回答的起点;)

标签: css css-grid


【解决方案1】:

这个答案有四个部分。前三个有助于解释第四个,它涵盖了额外列的原因。如果您只对答案感兴趣,请跳到最后。

内容:

  1. 不只是看起来:还有一个额外的行!
  2. grid-area 属性。
  3. grid-template-areas 属性。
  4. 未参考网格区域的放置。

1。不止是眼前一亮:还有一排!

您只是部分地定义了问题。是的,有一个额外的列。但是还有一个额外的行。

因为您没有在网格容器上定义高度,所以高度默认为 auto - 内容的高度 (more details)。因此,任何没有内容的行都会折叠并且不可见。

宽度不存在此问题,因为在这种情况下,您使用的是块级容器(由 display: grid 创建),默认情况下,它旨在消耗其父级的全部宽度(@ 987654322@).

这就是为什么您没有看到多余的行。如果你给容器一些高度,就会出现该行。

body {
  display: grid;
  grid-template-areas:
    "header"
    "footer";
  height: 150px; /* new */ 
}

body {
  display: grid;
  grid-template-areas:
    "header"
    "footer";
  height: 150px; /* new */
}

header {
  grid-area: header;
  background: aqua;
}

main {
  grid-area: main;
  background: darkorange;
}

footer {
  grid-area: footer;
  background: lightgreen;
}
<header>Header</header>
<main>Main</main>
<footer>Footer</footer>

注意:如果您使用了display: inline-grid,额外的行和额外的列都将不可见。

body {
  display: inline-grid;
  grid-template-areas:
    "header"
    "footer";
}

body {
  display: inline-grid; /* adjustment */
  grid-template-areas:
    "header"
    "footer";
}

header {
  grid-area: header;
  background: aqua;
}

main {
  grid-area: main;
  background: darkorange;
}

footer {
  grid-area: footer;
  background: lightgreen;
}
<header>Header</header>
<main>Main</main>
<footer>Footer</footer>

2。 grid-area 属性。

grid-area 属性命名会为该区域的每一侧创建一个named line

例如,grid-area: header 按顺序解析,如下所示:

  • grid-row-start: header
  • grid-column-start: header
  • grid-row-end: header
  • grid-column-end: header

marginborderpadding 一样,grid-area 属性是一个简写属性。与这些属性不同,grid-area 具有逆时针解析顺序(在 LTR 语言中),如上图所示。

因为命名的网格区域占用空间,它们需要存在的行和列。因此,命名的网格区域总是会影响布局,即使在 grid-template-areas 中没有引用它们也是如此。

因此,“修复”您的布局所需要做的就是删除 grid-area: main

main {
  /* grid-area: main; */
  background: darkorange;
}

body {
  display: grid;
  grid-template-areas:
    "header"
    "footer";
}

header {
  grid-area: header;
  background: aqua;
}

main {
  /* grid-area: main; */
  background: darkorange;
}

footer {
  grid-area: footer;
  background: lightgreen;
}
<header>Header</header>
<main>Main</main>
<footer>Footer</footer>

3。 grid-template-areas 属性。

使用grid-template-rowsgrid-template-columnsgrid-template-areas 创建的行和列(a/k/a 轨道)属于显式网格。任何未被这些属性定义的轨道都属于隐式网格 (source)。

对于grid-template-areas 中列出的每个字符串,都会创建一个新行。

对于字符串中的每个名称或点序列 (...),都会创建一个新列(但这不适用于这种情况,因为每个字符串只有一个名称)。

您的代码创建了一个包含两行一列的显式网格:

body {
  display: grid;
  grid-template-areas: 
    "header"
    "footer";
}

如图所示,headerfooter 有自己的行并存在于第一列中,与grid-template-areas 中的定义完全相同。

额外的两行和两列是隐式网格的一部分。

我们可以通过调整它们的大小来验证这一点。

grid-template-columns 仅适用于显式列。

grid-auto-columns主要在隐式列上工作(请参阅下面的注释)。

body {
  display: grid;
  grid-template-areas: "header" "footer";
  grid-template-columns: 1fr;
  grid-auto-columns: 100px;
  grid-template-rows: 100px 100px;
  grid-auto-rows: 25px;
}

body {
  display: grid;
  grid-template-areas:
      "header"
      "footer";
  grid-template-columns: 1fr;
  grid-auto-columns: 100px;
  grid-template-rows: 100px 100px;
  grid-auto-rows: 25px;
}

header {
  grid-area: header;
  background: aqua;
}

main {
  grid-area: main;
  background: darkorange;
}

footer {
  grid-area: footer;
  background: lightgreen;
}
<header>Header</header>
<main>Main</main>
<footer>Footer</footer>

注意:如果网格项目是使用grid-template-areas(创建明确的轨道)放置的,但它们的大小没有使用grid-template-columns / grid-template-rows,那么grid-auto-columns / grid-auto-rows apply to them. (second paragraph)

body {
  display: grid;
  grid-template-areas:
      "header"
      "footer";
  grid-auto-columns: 100px;
  grid-auto-rows: 25px;
}

body {
  display: grid;
  grid-template-areas:
      "header"
      "footer";
  grid-auto-columns: 100px;
  grid-auto-rows: 25px;
}

header {
  grid-area: header;
  background: aqua;
}

main {
  grid-area: main;
  background: darkorange;
}

footer {
  grid-area: footer;
  background: lightgreen;
}
<header>Header</header>
<main>Main</main>
<footer>Footer</footer>

4。未参考的网格区域的放置。

注意:老实说,我有 75% 的把握这部分是完全正确的。规范语言对我来说不是 100% 清楚。欢迎提供反馈、更正和更准确的答案。

在您的代码中,您有第三个网格区域,在 grid-template-areas 中没有引用。

body {
  display: grid;
  grid-template-areas: 
    "header"
    "footer";
}

main {
  grid-area: main;
  background: darkorange;
}

grid-area: main 去哪儿了?

正如我们已经看到的,它被发送到隐式网格中,两列两行。

网格区域由grid auto-placement algorithm处理,看起来是这样说的:

  1. 因为grid-area: main 没有明确定义(参见上面的第 3 节),所以它属于隐式网格。

  2. 因为网格列第 2 行和网格行第 3 行(显式网格的边界)是命名网格线,所以必须在隐式网格中创建新线以容纳 grid-area: main 的四个命名线。这只会发生在中间有空行和空列的情况下,将显式网格与自动放置的隐式网格区域分开。

【讨论】:

  • 在我尝试这个之前似乎是合乎逻辑的:jsfiddle.net/n7ja89zf/3 ...按照你所说的我们不应该重叠,因为浏览器会尽量避免在同一行有命名行。
  • 更令人惊讶的是:jsfiddle.net/n7ja89zf/5 .. 我想我今晚不会看我的电影了。
  • 最后,你走上了正轨,你的解释是正确的。我添加了一个答案以更好地突出您的观点并展示更多关于隐式行的示例。这是一种循序渐进的解释,直到我们得到最终结果。
  • 有趣;我不知道隐式网格轨道角不能触及显式网格轨道角(假设你的最后一部分是正确的)。似乎是一个不太理想的设计。
  • @TylerH 实际上他们正在接触,因为中间的空列在隐式网格内。诀窍是不能在另一个显式行的顶部定义隐式行,因此我们在逻辑上将有一个隐式轨道来分隔它们(空列)
【解决方案2】:

这是对 Michael_B already said 的扩展,以突出显示隐式网格线的创建方式。

让我们从一个简单的例子开始:

.container {
  width:100px;
  display: inline-grid;

  grid-auto-rows: 40px;
  border: 1px solid;

}
header {
  grid-row-start: header;
  background: blue;
}
<div class="container">
  <header>H</header>
</div>

我们有一个网格项,我们只设置了grid-row-start,最终结果是两行,其中一行是空的。两者都在隐式网格中,因为我们没有定义任何显式网格。

要了解发生了什么,让我们参考the specification

grid-template-rows、grid-template-columns 和 grid-template-areas 这三个属性共同定义了网格容器的显式网格。 ... 如果这些属性没有定义任何显式轨迹,显式网格仍然在每个轴上包含一条网格线ref

所以即使我们什么都不定义,我们仍然有一个明确的两线网格。这非常重要,因为没有这些行,我们就没有空行。

现在解释grid-row-start:header的部分:

&lt;custom-ident&gt;

第一次尝试将网格区域的边缘与命名网格区域匹配:如果存在名称为 ''-start (for grid--start) / -end'' (for grid--end),将第一条这样的行贡献给网格项的位置。

否则,将其视为整数 1 已与 &lt;custom-ident&gt; 一起指定。

很明显,我们会陷入否则并且会有grid-row-start:header 1

&lt;integer&gt; &amp;&amp; &lt;custom-ident&gt;?

第N条网格线贡献给网格项的放置...

如果名称以&lt;custom-ident&gt; 的形式给出,则仅计算具有该名称的行。如果没有存在足够多的具有该名称的线,则假定所有隐式网格线都具有该名称以找到该位置

在我们的例子中,我们没有足够的具有该名称的行(我们根本没有任何行)所以我们应该添加至少一行具有该名称并尝试放置我们的元素,因为整数是肯定的,该项目将被放置在该行下方:

网格有一条默认线(红色的),header 的使用会在其下方生成一个新的隐式线(由于自动添加了默认值1),并且元素将放置在该线下方创建多出一行。

如果我们使用-1,最后只有一行:

.container {
  width:100px;
  display: inline-grid;

  grid-auto-rows: 40px;
  border: 1px solid;

}
header {
  grid-row-start: header -1;
  background: blue;
}
<div class="container">
  <header>H</header>
</div>

在这种情况下,隐式行在显式行上方生成,而我们的元素放置在两行之间。

如果给定一个负整数,它会从显式网格的末端边缘开始反向计数

同时使用-11会给我们以下结果:

.container {
  width:100px;
  display: inline-grid;

  grid-auto-rows: 40px;
  border: 1px solid;

}
header {
  grid-row-start: header -1;
  background: blue;
}

footer {
  grid-row-start: header 1;
  background: red;
}
<div class="container">
  <header>H</header>
  <footer>F</footer>
</div>

这是另一个包含多个项目的示例,以说明 所有隐式网格线都假定具有该名称

.container {
  width:100px;
  display: inline-grid;

  grid-auto-rows: 40px;
  border: 1px solid;

}
header {
  grid-row-start: header 1;
  background: blue;
}
main {
  grid-row-start: main 1;
  background: red;
}
footer {
  grid-row-start: footer -1;
  background: green;
}
extra {
  grid-row-start: extra 5;
  background: orange;
}
<div class="container">
  <header>H</header>
  <main>M</main>
  <footer>F</footer>
  <extra>E</extra>
</div>

在这个例子中,我们总共需要 6 行隐式行,因为所有使用的整数都在 [-1,5] 范围内(不包括无效值 0),并且,为了放置每个元素,所有这些行都将具有为每个元素定义的名称。这就是为什么具有相同编号的两个元素将位于同一行(如 mainheader),因为即使名称不同,参考行也会相同。


现在让我们在前面的示例中添加grid-row-end

.container {
  width:100px;
  display: inline-grid;

  grid-auto-rows: 40px;
  border: 1px solid;

}
header {
  grid-row-start: header;
  grid-row-end: header;
  background: blue;
}
<div class="container">
  <header>H</header>
</div>

什么都不会发生,并且会产生完全相同的结果,因为:

如果起始行等于结束行,则删除结束行。 ref

我们换个名字吧:

.container {
  width:100px;
  display: inline-grid;

  grid-auto-rows: 40px;
  border: 1px solid;

}
header {
  grid-row-start: header;
  grid-row-end: foo;
  background: blue;
}
<div class="container">
  <header>H</header>
</div>

仍然是相同的结果,因为两者仍然相等(是的,它们相等!)。这两个值都将等于&lt;name&gt; 1,因此它们都只需要一个隐式行。然后浏览器将创建一个具有两个不同名称的隐式行,从而使我们的值相等。

让我们改变一个的值:

.container {
  width:100px;
  display: inline-grid;

  grid-auto-rows: 40px;
  border: 1px solid;

}
header {
  grid-row-start: header 1;
  grid-row-end: foo 2;
  background: blue;
}
<div class="container">
  <header>H</header>
</div>

同样的结果,但使用“不同”的代码。在这种情况下,我们将有 2 条隐式线,我们的元素将放置在它们之间。

当涉及到隐式网格时,这个名称基本上是不相关的,因为它们都将共享相同的行。只有当我们在显式网格中定义它们时才有意义:

.container {
  width:100px;
  display: inline-grid;

  grid-auto-rows: 40px;
  border: 1px solid;

}
header {
  grid-row-start: hello 1;
  grid-row-end: john 3;
  background: blue;
}
main {
  grid-row-start: main 1;
  grid-row-end: hi 2;
  background: red;
}
footer {
  grid-row-start: footer 2;
  grid-row-end: custom 5;
  background: green;
}
extra {
  grid-row-start: extra 3;
  grid-row-end: fsdfsdfsdfsd 5;
  background: orange;
}
<div class="container">
  <header>H</header>
  <main>M</main>
  <footer>F</footer>
  <extra>E</extra>
</div>

在上面的例子中,你可以用任何随机字符串更新名字,你总是会得到相同的结果;它只取决于整数:

考虑到grid-column-*,上述所有行为在逻辑上都是相同的。


现在,我们拥有了解初始示例所发生的一切所需的一切。

首先我们有如下的显式网格:

body {
  display: grid;
  grid-template-areas: 
     "header"
     "footer";
  /* No relevant but to better illustrate*/
  grid-auto-rows:50px;
  grid-auto-columns:50px;
}

header {
  grid-area: header;
  background: lightblue;
}

/*main {
  grid-area: main;
  background: darkorange;
}*/

footer {
  grid-area: footer;
  background: blue;
}
<header>Header</header>
<!--<main>Main</main>-->
<footer>Footer</footer>

grid-template-areas 属性从模板中的命名网格区域创建隐式命名线。对于每个命名网格区域 foo,创建四个隐式命名行:两个命名为foo-start,命名命名网格区域的行开始和列开始行,两个命名为foo-end,命名行结束和列- 命名网格区域的结束线。 ref

现在如果我们用grid-area:main;添加第三个元素,这意味着我们有

grid-row-start:main 1;
grid-row-end:main 1;
grid-column-start:main 1;
grid-column-end:main 1;

我们删除了*-end,因为它们等于*-start

grid-row-start:main 1
grid-column-start:main 1

根据前面的解释,我们需要一个额外的隐式行main,我们的元素将被放置在水平元素的下方和垂直元素的右侧:

body {
  display: grid;
  grid-template-areas: 
     "header"
     "footer";
  /* No relevant but to better illustrate*/
  grid-auto-rows:50px;
  grid-auto-columns:50px;
}

header {
  grid-area: header;
  background: lightblue;
}

main {
  grid-area: main;
  background: darkorange;
}

footer {
  grid-area: footer;
  background: blue;
}
<header>Header</header>
<main>Main</main>
<footer>Footer</footer>

如果我们删除grid-auto-*,行将是其内容的高度,使footer-endmain 之间的行为空。该列将分割网格元素的宽度,网格元素是一个具有全宽的块元素。这就是为什么您只看到一个额外的列而不是额外的行:

body {
  display: grid;
  grid-template-areas: 
     "header"
     "footer";
}

header {
  grid-area: header;
  background: lightblue;
}

main {
  grid-area: main;
  background: darkorange;
}

footer {
  grid-area: footer;
  background: blue;
}
<header>Header</header>
<main>Main</main>
<footer>Footer</footer>

另一个有趣的观察是,如果您使用grid-area:&lt;name&gt; 添加更多元素,它们将全部位于彼此之上:

body {
  display: grid;
  grid-template-areas: 
     "header"
     "footer";
}

header {
  grid-area: header;
  background: lightblue;
}

main {
  grid-area: main;
  background: darkorange;
}

footer {
  grid-area: footer;
  background: blue;
}

extra {
  grid-area: extra;
  background: red;
  opacity:0.8;
}
more {
  grid-area: more;
  background: green;
  opacity:0.3;
}
<header>Header</header>
<main>Main</main>
<footer>Footer</footer>
<extra>E</extra>
<more>More</more>

根据前面的解释,他们都会有以下几点:

grid-row-start:<name> 1;
grid-column-start:<name> 1;

由于编号相同(名称与我们已经解释的无关)它们都属于同一区域。

【讨论】:

  • “基本上这个名字与隐式网格无关,因为它们都将共享相同的行。只有当我们在显式网格中定义它们时才有意义。”实际上,名称在任何地方都无关紧要,因为名称只是一种用户友好的方式来引用数值,这是浏览器在创建网格期间使用的值。专注于名字有点牵强。
  • @TylerH 不是真的任何地方。如果您最初使用显式名称定义网格区域或网格线,它们在显式网格内是相关的。在这种情况下,数字将无关紧要,而名称将与查找职位相关。请注意我的回答中“第一次尝试匹配..”的部分,如果没有匹配,我们将陷入浏览器添加默认整数 1 的情况。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-15
  • 2020-02-04
  • 2020-06-05
  • 2021-10-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多