下拉列表動畫(JavaScript實現)


要實現一個帶動畫效果的下拉列表,通常的做法是,將外部容器的高度設為一個固定值,設置其overflow為hidden, 同時為其應用CSS過渡屬性,點擊相應的按鈕后改變外部容器的高度,如下面的實現。

<div id="p11-1-btn" onclick="toggleByHeight()">▼點我展開</div>
<ul id="p11-1" style="height: 0px;">
  <li style="background: #FF6666">第一</li>
  <li style="background: #FF9900">第二</li>
  <li style="background: #CCFF00">第三</li>
  <li style="background: #CC3399">第四</li>
</ul>
function toggleByHeight() {
  const containerStyle =  document.querySelector('#p11-1').style;
  const buttonExpand = document.querySelector('#p11-3-btn');
  if (containerStyle.height == '0px') {
      containerStyle.height = '120px';
      buttonExpand.innerText = '▲點我收起';
  } else {
    containerStyle.height = '0px';
    buttonExpand.innerText = '▼點我展開 ';
  }
}
#p11-1 {
  overflow:hidden;
  transition: height 1s;
  margin: 0px;
}

#p11-1 li {
  font-size: 16px;
  color: #ffffff;
  line-height: 30px;
  text-align: center;
  margin: 0px;
}

#p11-1-btn {
  cursor: pointer;
  background-color: #dcdcdc;
}

flex-basis

  上面的方法只適用於內部元素高度已知的情況。我們知道每個 li 的行高為30px, 由於 li 沒有邊框、內邊距、外邊距,則每個li 的高度就是30px, 全部展開后總高度為150px.
  但是現實中,我們並不總能知道內部元素的高度,而 display: nonedisplay: auto 之間是無法應用過渡效果的(具體為啥我也不知道),因此這種方法就不適用了,那么內部元素的高度應該如何計算出來呢?有人會說可以為父元素設置最大高度 max-height, 這樣只要 max-height 的值大於內部元素的總高度就行,但這樣做要求我們提供的 max-height 值不能過小也不能過大,過小則內部元素不能全部顯示出來,過大則動畫會出現延時。

  更好的方法是用JS去計算內部元素的高度,假設有如下的HTML結構,雖然外部容器的高度為0px, 但內部每個元素的高度仍舊為其原來的高度, 只是它被隱藏了起來而已,我們可以通過JS去計算內部元素的總高度。

<div id="p11-2" style="height: 0px;  overflow: hidden;">
  <div style="height: 40px; padding:10px">第一</div>
  <div style="height: 80px;">第二</div>
  <div style="height: 60px;">第三</div>
</div>

<script>
  const parent = document.querySelector('#p11-2');
  console.log(parent.style.height)  // 0px
  const childs = document.querySelectorAll('#p11-2 div');
  let totalHeight = 0;
  childs.forEach(child => {
    totalHeight += parseInt(child.style.height);
  });
  console.log(totalHeight)  // 180
</script>

  需要注意的是,你必須使用內聯樣式為元素指定高度,使用內部樣式表或者外部樣式表是無法正確通過JS獲取高度值的。解決這個問題的辦法是,獲取該元素的 offsetHeight 而不是 height, offsetHeight 包含該元素的內容高度、垂直內邊距和邊框,這樣一來,不管內部元素有沒有內邊距、邊框,我們總能正確的計算其高度。

offsetHight

  下面利用這種方法重寫一下上面的程序。

<div id="p11-3-btn" onclick="toggleByOffsetHeight()">▼點我展開</div>
<ul id="p11-3" style="height: 0px;">
  <li style="background: #FF6666; padding: 10px 0px">第一</li>
  <li style="background: #FF9900; border: solid 5px red">第二</li>
  <li style="background: #CCFF00; padding: 15px 0px">第三</li>
  <li style="background: #CC3399; border: double 3px blue">第四</li>
</ul>
function toggleByOffsetHeight() {
  const containerStyle = document.querySelector('#p11-3').style;
  const childs =  document.querySelectorAll('#p11-3 li');
  const buttonExpand = document.querySelector('#p11-3-btn');
  let totalHeight = 0;
  childs.forEach(child => {
    totalHeight += parseInt(child.offsetHeight);
  });
  if (containerStyle.height == '0px') {
      containerStyle.height = `${totalHeight}px`;
      buttonExpand.innerText = '▲點我收起';
  } else {
    containerStyle.height = '0px';
    buttonExpand.innerText = '▼點我展開 ';
  }
}
#p11-3 {
  overflow:hidden;
  transition: height 1s;
  margin: 0px;
}

#p11-3 li {
  font-size: 16px;
  color: #ffffff;
  line-height: 30px;
  text-align: center;
  margin: 0px;
}

#p11-3-btn {
  cursor: pointer;
  background-color: #dcdcdc;
}

flex-basis

  可以看到,無論內部元素占據的高度是什么,我們總能實現一個流暢的下拉列表過渡動畫。

該篇博客內的代碼已同步到Github

參考資料:
[1]. MDN文檔 https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLElement/offsetHeight


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM