使用纯JS实现max-height的动画效果?

6

我想要动画化一个div的高度。通常情况下,可以通过动画化max-height属性来实现。

然而,我需要在JS中实现这个效果。该div包含经常变化的动态内容,因此实际高度无法提前确定。

这是一个jsfiddle链接:https://jsfiddle.net/vpknxuk8/

var box = document.getElementById("box");
var button = document.getElementById("button");
var expanded = true;

button.addEventListener('click', function() {
    if (expanded) {
        box.style.maxHeight = 0;
        expanded = false;
    } else {
        box.style.maxHeight = "";
        expanded = true;
    }
});
#box {
  margin-top: 20px;
  border: 1px solid black;
  background-color: #4f88ff;
  width: 200px;
  transition: max-height 0.25s linear;
  overflow: hidden;
}
<button id="button">Click</button>

<div id="box">
  Hello World<br>
  This is dynamic content<br>
  The actual height won't be known ahead of time
</div>

盒子应该在高度为0和默认高度之间转换,但不知何故它没有动画效果。
其他关于此问题的StackOverflow版本仅涉及CSS-only解决方案或jQuery解决方案。
5个回答

27

不要使用max-height,除非你想采用仅CSS的松散解决方案,这会产生延迟间隙问题 - 这就是你首先求助于JS的原因,希望如此。

所以,使用heighttransition: height代替

function slidetoggle() {

  document.querySelectorAll(this.getAttribute('data-slidetoggle')).forEach(el => {
    const ch = el.clientHeight,
      sh = el.scrollHeight,
      isCollapsed = !ch,
      noHeightSet = !el.style.height;

    el.style.height = (isCollapsed || noHeightSet ? sh : 0) + "px";
    if (noHeightSet) return slidetoggle.call(this);
  });
}


document.querySelectorAll("[data-slidetoggle]").forEach(el => el.addEventListener('click', slidetoggle));
#box1,
#box2 {
  overflow: hidden;
  margin-bottom: 20px;
  background-color: #4f88ff;
  width: 200px;
  transition: height 0.5s;
}
<button data-slidetoggle="#box1">Toggle Box 1</button>

<div id="box1">
  Hello World<br> This is dynamic content<br> The actual height won't be<br> known ahead of time
</div>

<button data-slidetoggle="#box2">Toggle Box 2</button>


<div id="box2">
  Hello World<br> This is dynamic content<br> The actual height won't be<br> known ahead of time<br> bla
  <br> bla
  <br> bla
</div>

TIP: 如果您需要填充,我的建议是将内容包裹在另一个具有填充的子框中。

上面的代码是概念性和最小化的 - 还有很大的改进空间,但希望您已经理解了。

为什么使用上述方法,而不使用以下方法的解释:

如果我们不设置初始高度,则CSS高度过渡无法工作。
因此,基本上在单击时 - 如果不存在这样的高度,我们需要通过JS设置它。
设置这样的高度后,转换仍然无法起作用,因为浏览器还没有时间计算从其元素的盒模型过渡的时间:

// (INVALID DEMO) 
//
// Say the element is expanded.
// We need a height to transition-from
el.height = calculatedHeight +"px";  // set CSS height to the calculated value
// Transition to 0px height....
el.height = 0 +"px";
// NO DEAL. our element just snapped to 0 without transition

因此我们需要采用某种回调方式。
一种常见但不够严谨的方法是使用setTimeout()回调函数。
// (INVALID DEMO) 
//
// Say the element is expanded.
// We need a height to transition-from
el.height = calculatedHeight +"px";  // set CSS height to the calculated value
// Transition to 0px height giving the browser some ready-state hack
setTimeout(function() {
    // Eventually the box model will be calculated 
    el.height = 0 +"px";
}, 0);   // <<< PROBLEM: Should we use 0 ? 10? 100? 1000? 
// and our element beautifully transitioned to 0 height.
// Eventually :(

1
太棒了@roko-c-buljan。感谢您提供的代码和有帮助的评论 :) - Greg
1
哇,这太聪明了...非常感谢!!!!!我一直在与max-height和setTimeout搏斗,直到我发现了你超级聪明的方法 :) - nerdess

2
我首先检测元素的基本高度,并使用它来处理过渡,因为过渡需要高度才能工作。请保留HTML标记。

var box = document.getElementById("box");
var button = document.getElementById("button");
var expanded = true;

var height = box.offsetHeight;
box.style.height = height+"px";

button.addEventListener('click', function() {
    if (expanded) {
        box.style.height = 0;
        expanded = false;
    } else {
        box.style.height = height+"px";
        expanded = true;
    }
});
#box {
  margin-top: 20px;
  border: 1px solid black;
  background-color: #4f88ff;
  width: 300px;
  transition: height 0.25s;
  overflow: hidden;
}
<button id="button">
Click
</button>

<div id="box">
Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
</div>


1

试试这个:

var box = document.getElementById("box");
var button = document.getElementById("button");

//get your dynamic div's height:
var clientHeight = document.getElementById('box').clientHeight;
var expanded = true;
//define max-height property your div before collapse it. 
//otherwise it collapse without transition.
box.style.maxHeight = clientHeight+"px";
button.addEventListener('click', function() {
    if (expanded) {
        box.style.maxHeight = 0;
        expanded = false;
    } else {
        //set height
        box.style.maxHeight = clientHeight+"px";
        expanded = true;
    }
});

您可以根据实际情况使用 .scrollHeight.offsetHeight,而不是 .clientHeight。有关这些属性的详细信息,请参阅此评论:https://dev59.com/sWEh5IYBdhLWcg3wNxEn#22675563

0

以下类似的代码应该可以解决问题:

<div id="box">
Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
</div>

#box {
  margin-top: 20px;
  border: 1px solid black;
  background-color: #4f88ff;
  height:0px;
  width: 300px;
  transition: height 0.25s;
  overflow: hidden;
}

请求动画帧时执行逻辑:

 requestAnimationFrame(function () {
    let boxDiv = document.getElementById("box");
    boxDiv.style.height = "200px";
   });

为什么要像“200px”一样硬编码任意高度呢?这不会像OP所请求的那样切换(展开/折叠)。这容易出错,难以维护。 - Roko C. Buljan

-1

你有两个选项:

  • 过渡边框和填充,使它们全部一起折叠。

  • 将目标 div 包装在一个新容器中,并动画化该容器的高度。你需要使用 overflow: hidden;。


1
“转换边框和填充”是什么意思? - Joey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接