内容高度可变的可折叠面板

Collapsible panel with variable content height

几个选项卡的内容高度不同(例如,一个比另一个的文本多),但在子选项卡之间切换时,父级可折叠面板不会调整大小。如何将父级可折叠面板内容更改为与子选项卡高度匹配的最大高度?或者根据标签高度设置最大高度?

编辑 2:代码段现在包含 CSS 活动 class

编辑:找到半解决方案!
在 JavaScript 中,不要使用此代码:

content.style.maxHeight = content.scrollHeight + "px";

我改成如下:

content.style.maxHeight = "initial";

这是有效的,但它带来了一个新问题。因为现在 CSS 过渡不再像使用 scrollHeight 时那样工作:

.calendar-unfold {
    max-height: 0;
    transition: max-height 0.4s ease-out;
}

片段:

var coll = document.getElementsByClassName("calendar-fold");
  var i;
  for (i = 0; i < coll.length; i++) {
    coll[i].addEventListener("click", function () {
      this.classList.toggle("active");
      var content = this.nextElementSibling;
      if (content.style.maxHeight) {
        content.style.maxHeight = null;
      } else {
        content.style.maxHeight = content.scrollHeight + "px";
      }
    });
  }
  /* CALENDAR TABS 3*/
  function calendarTabCalendarBtn3(){
    document.getElementById('calendarTabCalendar3').style.display ='block';
    document.getElementById('calendarTabPruning3').style.display ='none';
    document.getElementById('calendarTabPregrow3').style.display ='none';
    document.getElementById('calendarTabAftergrow3').style.display ='none';
    document.getElementById('calendarTabNeighbour3').style.display ='none';
  }
  function calendarTabPruningBtn3(){
    document.getElementById('calendarTabCalendar3').style.display ='none';
    document.getElementById('calendarTabPruning3').style.display ='block';
    document.getElementById('calendarTabPregrow3').style.display ='none';
    document.getElementById('calendarTabAftergrow3').style.display ='none';
    document.getElementById('calendarTabNeighbour3').style.display ='none';
  }
  function calendarTabPregrowBtn3(){
    document.getElementById('calendarTabCalendar3').style.display ='none';
    document.getElementById('calendarTabPruning3').style.display ='none';
    document.getElementById('calendarTabPregrow3').style.display ='block';
    document.getElementById('calendarTabAftergrow3').style.display ='none';
    document.getElementById('calendarTabNeighbour3').style.display ='none';
  }
  function calendarTabAftergrowBtn3(){
    document.getElementById('calendarTabCalendar3').style.display ='none';
    document.getElementById('calendarTabPruning3').style.display ='none';
    document.getElementById('calendarTabPregrow3').style.display ='none';
    document.getElementById('calendarTabAftergrow3').style.display ='block';
    document.getElementById('calendarTabNeighbour3').style.display ='none';
  }
  function calendarTabNeighbourBtn3(){
    document.getElementById('calendarTabCalendar3').style.display ='none';
    document.getElementById('calendarTabPruning3').style.display ='none';
    document.getElementById('calendarTabPregrow3').style.display ='none';
    document.getElementById('calendarTabAftergrow3').style.display ='none';
    document.getElementById('calendarTabNeighbour3').style.display ='block';
  }
.calendar-fold {
    margin: 4px;
    padding: 10px;
    font-size: 1.25em!important;
    background-color: rgba(255,255,255,1.00);
    border: 1px solid rgba(55,175,75,1.00);
    color: rgba(55,175,75,1.00);
    border-radius: 5px;
    cursor: pointer;
    transition: 0.4s;
}
.calendar-fold:hover {
    border: 1px solid rgba(0,145,255,1.00);
    color: rgba(0,145,255,1.00);
}
.calendar-fold.active {
    background-color: rgba(55,175,75,1.00);
    border: 1px solid rgba(55,175,75,1.00);
    color: rgba(255,255,255,1.00);
}
.calendar-fold.active:hover {
    background-color: rgba(55,175,75,0.80);
    border: 1px solid rgba(55,175,75,0.00);
    color: rgba(255,255,255,1.00);
}
.calendar-unfold {
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.4s ease-out;
    background-color: rgba(200,200,200,1.00);
}
.calendar-tabbuttongroup {
    width: calc(100% - 8px);
    margin: 0 4px;
}
.calendar-tabbutton {
    background-color: rgba(55,175,75,1.00);
    color: rgba(255,255,255,1.00);
    padding: 8px;
    font-size: 1.00em;
    float: left;
    width: 10%;
    cursor: pointer;
}
.calendar-tabbutton:hover {
    background-color: rgba(55,175,75,0.80);
}
.calendar-tabgroup {
    float: left;
    width: 100%;
}
.calendar-tabcontent {
    width: 100%;
    padding: 0 0 8px 0;
}
#calendarTabCalendar3 { display: block; }
#calendarTabPruning3 { display: none; }
#calendarTabPregrow3 { display: none; }
#calendarTabAftergrow3 { display: none; }
#calendarTabNeighbour3 { display: none; }
<div class="calendar-fold">Collapsible</div>
<div class="calendar-unfold">
  <div class="calendar-tabbuttongroup">
    <div class="calendar-tabbutton calendarTabbtnCalendar" onclick="calendarTabCalendarBtn3();">Btn 1</div>
    <div class="calendar-tabbutton calendarTabbtnPruning" onclick="calendarTabPruningBtn3();">Btn 2</div>
    <div class="calendar-tabbutton calendarTabbtnPregrow" onclick="calendarTabPregrowBtn3();">Btn 3</div>
    <div class="calendar-tabbutton calendarTabbtnAftergrow" onclick="calendarTabAftergrowBtn3();">Btn 4</div>
    <div class="calendar-tabbutton calendarTabbtnNeighbour" onclick="calendarTabNeighbourBtn3();">Btn 5</div>
  </div>
  <div class="calendar-tabgroup">
    <div class="calendar-tabcontent" id="calendarTabCalendar3">
      <div class="calendar-moreinfo">
        <p class="calendar-pc">
          Some default content<br>
          Some default content<br>
          Some default content
          <hr>
        </p>
      </div>
    </div>
    <div class="calendar-tabcontent" id="calendarTabPruning3">
      <div class="calendar-moreinfo">
        <p class="calendar-pc">
          Some small content<br>
          Some small content
          <hr>
        </p>
      </div>
    </div>
    <div class="calendar-tabcontent" id="calendarTabPregrow3">
      <div class="calendar-moreinfo">
        <p class="calendar-pc">
          Some large content<br>
          Some large content<br>
          Some large content<br>
          Some large content<br>
          Some large content<br>
          Some large content
          <hr>
        </p>
      </div>
    </div>
    <div class="calendar-tabcontent" id="calendarTabAftergrow3">
      <div class="calendar-moreinfo">
        <p class="calendar-pc">
          Some medium content<br>
          Some medium content<br>
          Some medium content<br>
          Some medium content
          <hr>
        </p>
      </div>
    </div>
    <div class="calendar-tabcontent" id="calendarTabNeighbour3">
      <div class="calendar-moreinfo">
        <p class="calendar-pc">
          Some small content<br>
          Some small content
          <hr>
        </p>
      </div>
    </div>
  </div>
</div>

您可能会发现此修订版代码中的一些想法很有用。


需要注意的主要变化包括:

  • 此版本在按钮的父级 div 上使用 单个事件侦听器 (之所以有效,是因为 DOM 事件通常冒泡,而不是内联事件处理程序最多到祖先元素)。
  • 关于修改哪个元素的决定使用事件的 target 属性 加上 data-attribute每个按钮(告诉听众哪个选项卡与该按钮相关联)。
  • HTML 包括两个新的 类(它们是 CSS 中选择器的目标):hiddenhighlight。该脚本根据需要添加或删除这些 类,而不是手动操作样式。

这些更改有助于在页面上关注点分离(标记与样式与脚本)并允许减少重复代码,两者其中可以使您的代码更不容易出错并且更易于维护。


“选项卡组”div 会动态调整大小以适应其内容 正如您所期望的那样,无需通过显式样式更改其高度。

请参阅代码内注释以获得进一步的解释(并注意一些原始的 HTML 和 CSS 已被更改或为简洁明了而省略。)

// Identifies some DOM elements
const
  buttonGroup = document.getElementsByClassName("tabbuttongroup")[0],
  buttons = document.getElementsByClassName("tabbutton");
  tabs = document.querySelectorAll(".tabgroup .tabcontent");

// Calls `updateTabsDisplay` when user clicks inside buttonGroup
buttonGroup.addEventListener("click", updateTabsDisplay);

// Defines listener (which always has access to the triggering event)
function updateTabsDisplay(event){
  const clicked = event.target; // Event's `target` property is useful

  // Ignores irrelevant clicks
  if(!clicked.classList.contains("tabbutton")){ return; }

  // Un-highlights buttons, and highlights clicked button
  for(let button of buttons){
    button.classList.remove("highlight");
    if(button == clicked){
      clicked.classList.add("highlight");
    }
  }

  // Reads button's `data-tab` attribute, where associated tabClass is stored
  const tabClass = clicked.dataset.tab;

  // Hides tabs, and un-hides the matching tab
  for(let tab of tabs){
    tab.classList.add("hidden");
    if(tab.classList.contains(tabClass)){
      tab.classList.remove("hidden");
    }
  }
}
.tabbutton { background-color: rgba(55,175,75,1.00); color: white; padding: 8px; float: left; width: 12ch; text-align: center; }
.tabbutton + .tabbutton { border-left: 1px solid white; }
.tabbutton:hover { background-color: rgba(55,175,75,0.80); }
.tabbutton.highlight { background-color: rgba(55,175,75,0.50); }

.tabgroup { clear: both; }
.tabcontent { padding: 0 0 8px 0; }

.hidden{ display: none; } 
<div class="tabbuttongroup">
  <!-- `data-attributes` can be used to store relevant strings -->
  <div class="tabbutton highlight" data-tab="tab-calendar">Calendar</div>
  <div class="tabbutton" data-tab="tab-pruning">Pruning</div>
  <div class="tabbutton" data-tab="tab-pregrow">Pregrow</div>
  <div class="tabbutton" data-tab="tab-aftergrow">Aftergrow</div>
  <div class="tabbutton" data-tab="tab-neighbour">Neighbour</div>
</div>

<div class="tabgroup">
  <!-- Each tab has a class matching its button's `data-tab` attribute -->
  <div class="tabcontent tab-calendar">
    <p>"Calendar Tab" content<br/>..<br/>..<br/>..<hr></p>
  </div>
  <div class="tabcontent tab-pruning hidden">
    <p>"Pruning Tab" content<hr></p>
  </div>
  <div class="tabcontent tab-pregrow hidden">
    <p>"Pregrow Tab" content<br/>..<br/>..<br/>..<br/>..<br/>..<br/>..<hr></p>
  </div>
  <div class="tabcontent tab-aftergrow hidden">
    <p>"Aftergrow Tab" content<br/>..<br/>..<br/>..<br/>..<hr></p>
  </div>
  <div class="tabcontent tab-neighbour hidden">
    <p>"Neighbour Tab" content<hr></p>
  </div>
</div>