内容高度可变的可折叠面板
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 中选择器的目标):
hidden
和highlight
。该脚本根据需要添加或删除这些 类,而不是手动操作样式。
这些更改有助于在页面上关注点分离(标记与样式与脚本)并允许减少重复代码,两者其中可以使您的代码更不容易出错并且更易于维护。
“选项卡组”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>
几个选项卡的内容高度不同(例如,一个比另一个的文本多),但在子选项卡之间切换时,父级可折叠面板不会调整大小。如何将父级可折叠面板内容更改为与子选项卡高度匹配的最大高度?或者根据标签高度设置最大高度?
编辑 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 中选择器的目标):
hidden
和highlight
。该脚本根据需要添加或删除这些 类,而不是手动操作样式。
这些更改有助于在页面上关注点分离(标记与样式与脚本)并允许减少重复代码,两者其中可以使您的代码更不容易出错并且更易于维护。
“选项卡组”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>