【问题标题】:Why is scrollWidth not including a child element's right margin?为什么 scrollWidth 不包括子元素的右边距?
【发布时间】:2021-01-25 21:38:46
【问题描述】:

我正在尝试将 <div> 幻灯片打开到包含其内容而不会溢出的最小尺寸。

这是我目前所得到的:

window.onload = function() {
  setTimeout(() => {
    var thing = document.getElementById('thing');
    thing.style.maxWidth = thing.scrollWidth + "px";
    thing.style.maxHeight = thing.scrollHeight + "px";
  }, 1000);
}
body {
  background-color: black;
}

p {
  margin: 40px;
  padding: 0;
  color: yellow;
  background-color: green;
  font-family: monospace;
}

div.expander {
  margin: 0;
  padding: 0;
  max-width: 0;
  max-height: 0;
  overflow: hidden;
  transition:
    max-width 1s,
    max-height 1s;
  border: 1px solid red;
}

hr {
  margin: 0;
  padding: 0;
  border-top: 1px solid blue;
  border-right: none;
  border-bottom: none;
  border-left: none;
}
<div id="thing" class="expander">
  <p>Hello, world!</p>
  <hr>
  <p>Goodbye, world!</p>
</div>

看看&lt;p&gt; 的宽度如何不足以容纳其文本而不会溢出?这就是我试图阻止的。显然,scrollHeight 正在做我期望的事情。为什么不是scrollWidth

【问题讨论】:

  • 制作p {display:inline-block;}。我不确定为什么会这样。
  • 您希望将文本换行(2 行)吗?如果没有,请将white-space: nowrap; 添加到您的 p 样式中。

标签: javascript css


【解决方案1】:

这不是全部情况,但margin-right(或margin-left)在特定情况下会被忽略。这是 CSS 中的预期块功能。来自the specs

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

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

在我看来,这不是很直观的行为,我更愿意尽可能避免块布局的边距(原因不止于此)。因此,您可以在您的 &lt;p&gt; 标签周围添加一个包装器 &lt;div&gt; 并改用 padding。这也使您可以在项目之间使用边框,而不是在您的内容中添加&lt;hr&gt;。我认为proper semantics 可以采用任何一种方式,具体取决于您对它的实际使用情况。引用 MDN 文档:

HTML &lt;hr&gt; 元素代表段落级元素之间的主题中断:例如,故事中的场景变化,或章节中的主题转移。

从历史上看,这一直以水平规则或线的形式出现。虽然在可视化浏览器中它仍可能显示为水平线,但此元素现在是用语义术语而不是表示术语定义的,因此如果您希望绘制水平线,则应使用适当的 CSS 进行。

但是,虽然这解决了一些问题,但这并不能解决所有问题。 &lt;p&gt; 元素的计算宽度仍然小于min-content。因此,我们可以使用 min-width: min-content; 或使用不同的 display 值(可能在 expander 来强制执行此操作。这改变了用于计算引擎盖下宽度的算法。

完整示例代码之前的最后一个注释:max-widthmax-heightdecent trick,但如果您试图避免从 JS 修改样式,它们实际上只对这种事情有用(想想 :hover 或在 JS 中添加和删除 .open 类,而不是直接设置宽度和高度)

setTimeout(() => {
  const expander = document.querySelector('.js-expander');
  expander.style.width = expander.scrollWidth + "px";
  expander.style.height = expander.scrollHeight + "px";
}, 1000);
body {
  background-color: black;
}

.expander {
  display: grid;
  width: 0px;
  height: 0px;
  overflow: hidden;
  border: 1px solid red;
  transition:
    width 1s,
    height 1s;
}

.expander-item {
  padding: 40px;
  font-family: monospace;
  border-bottom: 1px solid blue;
}

.expander-item:last-child { border-bottom: 0px; }

.expander-item > * {
  color: yellow;
  background-color: green;
}

.expander-item > :first-child { margin-top: 0; }
.expander-item > :last-child { margin-bottom: 0; }
<div class="expander js-expander">
  <div class="expander-item">
    <p>Hello, world!</p>
  </div>
  <div class="expander-item">
    <p>Goodbye, world!</p>
  </div>
</div>

更新:基于 cmets,我想我可能会包括如何将 &lt;hr/&gt; 添加回:

setTimeout(() => {
  const expander = document.querySelector('.js-expander');
  expander.style.width = expander.scrollWidth + "px";
  expander.style.height = expander.scrollHeight + "px";
}, 1000);
body {
  background-color: black;
}

.expander {
  display: grid;
  width: 0px;
  height: 0px;
  overflow: hidden;
  border: 1px solid red;
  transition:
    width 1s,
    height 1s;
}

.expander-group {
  padding: 40px;
  font-family: monospace;
}

.expander-group > * {
  color: yellow;
  background-color: green;
}

.expander-group > :first-child { margin-top: 0; }
.expander-group > :last-child { margin-bottom: 0; }

.expander > hr {
  margin: 0;
  padding: 0;
  border-top: 1px solid blue;
  border-right: none;
  border-bottom: none;
  border-left: none;
}
<div class="expander js-expander">
  <div class="expander-group">
    <p>Hello, world 1!</p>
    <p>Hello, world 2!</p>
  </div>
  <hr>
  <div class="expander-group">
    <p>Goodbye, world!</p>
  </div>
</div>

【讨论】:

  • 总的来说我喜欢它——这是我最终目标的一个很好的起点,而且你让我对盒子模型内部发生的事情有了很多很好的了解......但是通过删除hr 并将边框附加到.expander-items,你改变了我的语义。蓝线不应该是它上面的div 的属性——我后来想堆叠多个项目,它们之间没有线,或者让我的expander 使用flex-direction: row,等等。这就是为什么我想要一个明确独立的元素 - 在我最初的问题中,我可能简化为一个太少的例子?
  • 很容易重新添加&lt;hr&gt; 标签,这就是为什么我提到它“可以采用任何一种方式”,但你刚才提到的对我来说听起来不像语义。所以,我会澄清一下以防万一:如果要点实际上是在段落之间包含一个主题中断,恰好显示为一条蓝线,我同意&lt;hr&gt; 标签可能属于项目之间。如果只是要“包含一条蓝线”,那不属于 HTML - 您可以在每个 .expander-item 中包含多个 &lt;p&gt; 标签(也许“组”会比“项目”更好的名称")
  • 还值得指出的是,这 - 可能只是因为它是一个最小的例子 - 看起来可能是nested lists
  • @ClaireNielsen 更新了答案以包括如何添加 &lt;hr&gt; 标记,如果您认为合适的话。
  • 今天回到这里,我想我会把这个链接放在这里以获取更多信息developer.mozilla.org/en-US/docs/Glossary/…
猜你喜欢
  • 2012-04-20
  • 1970-01-01
  • 2011-01-11
  • 1970-01-01
  • 1970-01-01
  • 2012-02-18
  • 1970-01-01
  • 1970-01-01
  • 2020-05-17
相关资源
最近更新 更多