51

我正在尝试制作两个 div,一个在另一个里面。内部 div 比外部 div 大,外部 div 有overflow:scroll,内部 div 有margin:25px。所以我这样做:

#outer {
    width: 200px;
    height: 100px;
    overflow: scroll;
}
#inner {
    width: 400px;
    height: 200px;
    margin: 25px;
}

...

<div id="outer">
    <div id="inner">

    </div>
</div>

内部 div 没有像预期的那样一直有 25px 的边距,而是在三边有 25px 的边距,但右侧没有。在我看来,这是非常违反直觉的。

如果我添加一个宽度足够大的中间 div 以包含内部 div + 50px,我们可以使它看起来正确,但这似乎是一个 hacky 解决方法。

请参阅我在 JSFiddle 上的示例:http: //jsfiddle.net/d3Nhu/16/

这在每个主要浏览器中都以相同的方式发生。这种行为有什么好的理由吗?根据 CSS 规范,这是正确的行为吗?

注意:正如您在此示例中所期望的那样,如果您使用overflow:auto而不是overflow:scroll.

编辑:请注意,我不是在寻找解决此行为的方法。(我已经找到了。)我正在寻找有关此行为原因的任何见解,特别是如果它在任何地方的CSS 规范中都有记录。

4

3 回答 3

70

TL;博士:

边距用于将元素从包装器中移入,而不是向外扩展包装器。

长解释:

此行为与在文档中的任何位置指定width除了水平之外的 a一致。margin要分解它,请考虑以下片段,其中我指定了一个没有overflow属性的包装器,并且margin不扩展包装器元素。

body {
    padding: 20px;
}
.outer {
    width: 400px;
    border: 1px solid black;
}
.inner {
    width: 400px;
    height: 40px;
    margin: 0 20px;
    background: grey;
}
<div class="outer">
    <div class="inner">
        
    </div>
</div>

如您所见,margin并没有导致包装器的大小扩大,元素只是继续溢出。此行为记录在 CSS 2.1 规范中的视觉格式化模型详细信息下。

摘自“可视化格式化模型详细信息”的“正常流程中的块级、非替换元素”部分:

在其他属性的使用值中必须保持以下约束:

'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' = 包含块的宽度

[...]

如果以上所有的计算值都不是“auto”,则这些值被称为“过度约束”,并且使用的值之一必须与其计算值不同。如果包含块的'direction'属性的值为'ltr',则忽略'margin-right'的指定值并计算该值以使等式成立。如果 'direction' 的值是 'rtl',这会发生在 'margin-left' 上。

这段摘录非常密集,所以简单来说,让我们忽略 和 的宽度borderpadding两者都是0,留下width,margin-leftmargin-right

现在,由于 and 有一个固定的andwidth值,因此这些值是“过度约束的”。现在在我们的示例中,由于默认方向是方向,因此强制进行补偿。margin-leftmargin-rightltrmargin-right

要查看方向的效果,让我们尝试向dir="rtl"包装元素添加一个属性。

body {
    padding: 20px;
}
.outer {
    width: 400px;
    border: 1px solid black;
}
.inner {
    width: 400px;
    height: 40px;
    margin: 0 20px;
    background: grey;
}
<div class="outer" dir="rtl">
    <div class="inner">
        
    </div>
</div>

现在元素溢出到左边。让我们看看这个dir="rtl"属性是否对您的overflow: scroll示例具有相同的效果。

#outer {
    border: 1px solid #00F;
    width: 200px;
    height: 100px;
    overflow: scroll;
}
#inner {
    border: 1px solid #F0F;
    margin: 25px;
    width: 400px;
    height: 200px;
}
<div id="outer" dir="rtl">
    <div id="inner">
    
    </div>
</div>

是的,确实如此。现在左边缺少边距,而不是右边。

但为什么不overflow: scroll包括边距?

主要是因为规范没有说应该。让我们看一下该overflow属性的 CSS 2 规范。

摘自“视觉效果”的“溢出和剪辑”部分:

每当发生溢出时,'overflow' 属性指定一个框是否被剪裁到其填充边缘,如果是,是否提供滚动机制来访问任何剪裁出来的内容。

看看它是如何具体说“剪掉的内容”的。关于“内容”的解释,让我们参考 CSS 2 规范中的下图。

来自“盒子模型”的“盒子尺寸”部分的图形:

盒子模型

正如我们所见, 与margin是分开的content。然而,此时值得注意的是,填充和边框包含在滚动区域中,所以当规范说“内容”时,它可能指的是边框框,或者至少,它似乎是这样解释的。

为什么display: inline-block有效?

基本上,边距在inline-block元素上的行为不同,因为它们是内容级别而不是块级别,并且它们没有“过度约束”的概念。

于 2015-02-23T20:16:43.067 回答
21

添加 display:inline-block;#innerdiv

看到这个小提琴

于 2012-07-27T20:47:09.903 回答
6

所以这里的答案实际上并不能解决问题!(虽然对它不起作用的原因非常详细)

我需要一个解决方案。这是我的未来读者。使用display:flex;伪 ::after 元素的组合来伪造 div 的存在以提供所需的边距。

.wrapper {
  display: flex;
  width: 400px;
  height: 100%;
  padding: 40px;
  background: lightGrey;
}

.lists_container {
  display: flex;
  flex-direction: row;
  justify-content: flex-start;
  overflow: auto;
  position: relative;
  background: grey;
  padding: 40px;
  margin: 40px;
  width: 100%;
}

.card {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-width: 250px;
  max-width: 500px;
  height: 100px;
  margin: 50px 0;
  padding: 20px;
  background: orange;
  margin-right: 30px;
}

.card.last::after {
  content: '';
  position: absolute;
  right: -100px;
  width: 40px;
  height: 100%;
  background: red;
}
<div class="wrapper">

  <div class="lists_container">

    <div class="card">
    </div>

    <div class="card">
    </div>

    <div class="card last">
    </div>

  </div>

</div>

于 2018-11-10T20:32:22.960 回答