【问题标题】:Why is the scrollHeight property for elements with hidden overflow not correct?为什么具有隐藏溢出的元素的 scrollHeight 属性不正确?
【发布时间】:2021-10-29 17:13:36
【问题描述】:

为避免出现 XY 问题,我应该解释一下我实际上想要做什么。我正在尝试动画显示和隐藏div。我的策略是将max-height 设置为scrollHeight 属性的计算高度并设置overflow: hidden。要隐藏它,我可以将max-height 设置为"0px",显示它我将它设置回scrollHeight,我可以在max-height 属性上设置动画。

需要明确的是,当div处于hidden状态时,它有max-height: 0,因为overflow: hidden它不会出现。在visible 状态下,它有一个max-height,应该完全等于它的正常高度。我还没有找到任何其他方法来做到这一点。

这似乎可行,但如果 div 最初是隐藏的,它就不起作用。

这是一个 MWE:

切换 1 和切换 2 是相同的。唯一的区别是切换 1 开始可见,切换 2 不可见。您可以看到 Toggle 1 按预期工作,但 Toggle 2 仅显示 div 的一半。这是因为scrollHeight 只是应有的一半。

Toggle 3 不包括 align-items: center,它更接近,但仍不完全正确,相差 3px。

form1 = document.getElementById('form1');
console.log('Setting form1 height to ' + form1.scrollHeight);
form1.style.maxHeight = form1.scrollHeight + "px";
function toggle1() {
  form1.classList.toggle('hide');
}

form2 = document.getElementById('form2');
console.log('Setting form2 height to ' + form2.scrollHeight);
form2.style.maxHeight = form2.scrollHeight + "px";
function toggle2() {
  form2.classList.toggle('hide');
}

form3 = document.getElementById('form3');
console.log('Setting form3 height to ' + form3.scrollHeight);
form3.style.maxHeight = form3.scrollHeight + "px";
function toggle3() {
  form3.classList.toggle('hide');
}
.form {
  display: flex;
  align-items: center;
  overflow: hidden;
  transition: max-height 0.3s linear;
}

.form3 {
  display: flex;
  overflow: hidden;
  transition: max-height 0.3s linear;
}

.hide {
  max-height: 0 !important;
}

.input {
  font-size: 28px;
}

.btn {
  font-size: 28px;
}
<button onclick="toggle1()">Toggle 1</button>
<div id="form1" class="form">
  <input class="input">
  <button class="btn">Submit</button>
</div>
<button onclick="toggle2()">Toggle 2</button>
<div id="form2" class="form hide">
  <input class="input">
  <button class="btn">Submit</button>
</div>
<button onclick="toggle3()">Toggle 3</button>
<div id="form3" class="form3 hide">
  <input class="input">
  <button class="btn">Submit</button>
</div>
<p>More content</p>

根据docs,这应该可以工作。

Element.scrollHeight 只读属性是元素内容高度的度量,包括由于溢出而在屏幕上不可见的内容。

div 没有任何填充或边框,因此看起来应该完全正确。

我不知道这里发生了什么。 这是一个错误吗? 这种行为至少存在于 macOS 上的 Firefox 90 和 Chrome 88 中。

有什么我不理解的基本原理使这不可能吗?

我不明白为什么align-items: center 很重要。这表明我没有足够的信息来推断这个问题。

或者我只是错过了我需要做的其他事情?

也许这完全可行,但我只是忘记了一些事情。


在结束时,参考我之前所说的,我真的只是想要一个开始隐藏的 div,我可以动画它打开/关闭。也许有不同的方法可以做到这一点。但在这一点上,我已经在这个问题上投入了足够多的精力,我真的很想知道这里发生了什么。

【问题讨论】:

    标签: css flexbox css-animations overflow


    【解决方案1】:

    一种解决方法是在没有隐藏类的情况下加载 form2,但使用 JS 立即添加它,您已经阅读了“正确的”scrollHeight。

    form2.classList.add('hide');
    

    form1 = document.getElementById('form1');
    console.log('Setting form1 height to ' + form1.scrollHeight);
    form1.style.maxHeight = form1.scrollHeight + "px";
    
    function toggle1() {
      form1.classList.toggle('hide');
    }
    
    form2 = document.getElementById('form2');
    console.log('Setting form2 height to ' + form2.scrollHeight);
    form2.style.maxHeight = form2.scrollHeight + "px";
    form2.classList.add('hide');
    
    function toggle2() {
      form2.classList.toggle('hide');
    }
    
    form3 = document.getElementById('form3');
    console.log('Setting form3 height to ' + form3.scrollHeight);
    form3.style.maxHeight = form3.scrollHeight + "px";
    
    function toggle3() {
      form3.classList.toggle('hide');
    }
    .form {
      display: flex;
      align-items: center;
      overflow: hidden;
      transition: max-height 0.3s linear;
    }
    
    .form3 {
      display: flex;
      overflow: hidden;
      transition: max-height 0.3s linear;
    }
    
    .hide {
      max-height: 0 !important;
    }
    
    .input {
      font-size: 28px;
    }
    
    .btn {
      font-size: 28px;
    }
    <button onclick="toggle1()">Toggle 1</button>
    <div id="form1" class="form">
      <input class="input">
      <button class="btn">Submit</button>
    </div>
    <button onclick="toggle2()">Toggle 2</button>
    <div id="form2" class="form">
      <input class="input">
      <button class="btn">Submit</button>
    </div>
    <button onclick="toggle3()">Toggle 3</button>
    <div id="form3" class="form3 hide">
      <input class="input">
      <button class="btn">Submit</button>
    </div>
    <p>More content</p>

    我意识到这不是一个完整的解释,但一个观察是在 form3 上仍然有一个 flex - 它消除了输入和按钮上的“自然”垂直居中,因此高度更小。如果您完全移除 flex,高度(和居中)会返回。

    搜索我怀疑但无法证明的完整解释,通过一个类设置隐藏,尽管有重要的设置,而内联设置最大高度会以某种方式改变浏览器计算滚动高度的顺序。

    【讨论】:

    • 太好了,行得通!太感谢了。旁注,我现在可以只使用clientHeight 而不是scrollHeight,因为整个div 现在是可见的吗?我问是因为我添加了一个颜色输入,现在scrollHeight 比它应该的大,但clientHeight 是正确的。
    【解决方案2】:

    如果我们查看scrolling area 的规范,就可以解释align-items:center 的行为。它说垂直滚动区域是从

    元素的顶部填充边缘。

    元素底部填充边缘的最底部边缘和元素所有后代盒子的底部边缘边缘,不包括将元素的祖先作为其包含块的盒子。

    注意不平衡。包括底部填充边缘下方的溢出,但不包括顶部填充边缘上方的溢出。因此,当 flex 容器的高度为 0 时,溢出的一半将在顶部填充边缘之上并且不计算在内。因此是 20px 而不是 40px。

    form3 的align-items:initial(即拉伸)行为可能更加微妙。高度来自按钮,输入字段要小得多。所以按钮由按钮元素生成的外框和按钮内部的“提交”文本生成的内框组成。内框从外框的顶部边框和内边距(3px)下方开始,高度为 34px。现在外框通过 max-height 值减小到 0px 高度,使内框溢出,从而将内框的底部边缘放置在 div 的顶部填充边缘下方 37px 处。遵循与上述相同的规则来调整滚动区域的大小,因此 scrollHeight 为 37px。

    我推荐@AHaworth 的解决方法来避免这个问题。


    以上像素测量值适用于 Firefox。 Chromium 的内容框尺寸略小,但遵循相同的规则。

    【讨论】:

    • 太好了,感谢您的分析!这是标题中问题的答案,所以我接受了,但我非常感谢@AHaworth 的解决方法。
    猜你喜欢
    • 2014-08-17
    • 2014-08-28
    • 2021-09-05
    • 2015-05-23
    • 2012-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多