Как сделать плавным раскрытие вертикального многоуровневого меню?
Есть вертикальное меню-каталог с неограниченным уровнем вложенности. Я столкнулся с проблемой - не могу анимировать его раскрытие и закрытие. Свойство display не анимируется, height тоже. Есть ещё вариант с max-height, но всё это слишком громоздко получается.
Вот пример, все пункты Sub имеют вложенное подменю.
document.querySelector('.root-nav').onclick = function(event) {
if (event.target.nodeName !== 'SPAN') return
closeAllSubMenu(event.target.nextElementSibling)
event.target.classList.add('submenu-active-span')
event.target.nextElementSibling.classList.toggle('submenu-active')
}
function closeAllSubMenu(currentMenu = null) {
const parents = []
if (currentMenu) {
let currentParent = currentMenu.parentNode
while(currentParent) {
if (currentParent.classList.contains('root-nav')) break
if (currentParent.nodeName === 'UL') parents.push(currentParent)
currentParent = currentParent.parentNode
}
}
const allSubMenu = document.querySelectorAll('.root-nav ul')
Array.from(allSubMenu).forEach(item => {
if (item !== currentMenu && !parents.includes(item)) {
item.classList.remove('submenu-active')
if (item.previousElementSibling.nodeName === 'SPAN') {
item.previousElementSibling.classList.remove('submenu-active-span')
}
}
})
}
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.root-nav {
width: 300px;
}
.root-nav li {
list-style-type: none;
background-color: coral;
padding-left: 0;
position: relative;
}
.root-nav a, .root-nav span {
text-decoration: none;
color: white;
display: block;
padding: 5px 10px;
cursor: pointer;
}
.root-nav ul {
display: none;
width: 100%;
}
ul.submenu-active {
display: block;
padding-left: 20px;
}
span.submenu-active-span {
background-color: crimson;
}
<ul class="root-nav">
<li><a href="#">Link 1</a></li>
<li>
<span>Sub 1</span>
<ul>
<li><a href="#">Link 10</a></li>
<li><a href="#">Link 20</a></li>
<li><a href="#">Link 30</a></li>
<li><a href="#">Link 40</a></li>
</ul>
</li>
<li><a href="#">Link 3</a></li>
<li><a href="#">Link 4</a></li>
<li>
<span>Sub 2</span>
<ul>
<li><a href="#">Link 50</a></li>
<li><a href="#">Link 60</a></li>
<li>
<span>Sub 20</span>
<ul>
<li><a href="#">Link 100</a></li>
<li><a href="#">Link 200</a></li>
<li>
<span>Sub 3</span>
<ul>
<li><a href="#">Link 1000</a></li>
<li><a href="#">Link 2000</a></li>
<li><a href="#">Link 3000</a></li>
<li><a href="#">Link 4000</a></li>
</ul>
</li>
</li>
<li><a href="#">Link 400</a></li>
</ul>
</li>
<li><a href="#">Link 80</a></li>
</ul>
</li>
</ul>
Может, знает кто как сделать плавное раскрытие вертикального меню с неограниченной вложенностью?
Ответы (1 шт):
Автор решения: Alexander Kiselev
→ Ссылка
Можно анимировать с помощью max-height + overflow: hidden.
Получится давольно красиво разворачивать подменю.
document.querySelector('.root-nav').onclick = function(event) {
if (event.target.nodeName !== 'SPAN') return
closeAllSubMenu(event.target.nextElementSibling)
event.target.classList.add('submenu-active-span')
event.target.nextElementSibling.classList.toggle('submenu-active')
}
function closeAllSubMenu(currentMenu = null) {
const parents = []
if (currentMenu) {
let currentParent = currentMenu.parentNode
while(currentParent) {
if (currentParent.classList.contains('root-nav')) break
if (currentParent.nodeName === 'UL') parents.push(currentParent)
currentParent = currentParent.parentNode
}
}
const allSubMenu = document.querySelectorAll('.root-nav ul')
Array.from(allSubMenu).forEach(item => {
if (item !== currentMenu && !parents.includes(item)) {
item.classList.remove('submenu-active')
if (item.previousElementSibling.nodeName === 'SPAN') {
item.previousElementSibling.classList.remove('submenu-active-span')
}
}
})
}
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.root-nav {
width: 300px;
}
.root-nav li {
list-style-type: none;
background-color: coral;
padding-left: 0;
position: relative;
border: 1px solid;
}
.root-nav a, .root-nav span {
text-decoration: none;
color: white;
display: block;
padding: 5px 10px;
cursor: pointer;
}
.root-nav ul {
height: 100%;
width: 100%;
padding-left: 20px;
max-height: 0px; /* <-- Устанавливаем по умолчанию максимальную высоту 0 */
transition: max-height 0.15s ease-out; /* <-- Добавляем плавность изменения */
overflow: hidden; /* <-- скрываем внутренний контент */
}
span.submenu-active-span {
background-color: crimson;
}
span.submenu-active-span+ul.submenu-active {
max-height: 500px; /* <-- раскрываем блок */
transition: max-height 0.25s ease-in; /* <-- добавляем правила плавности для закрытия меню */
}
<ul class="root-nav">
<li><a href="#">Link 1</a></li>
<li>
<span>Sub 1</span>
<ul>
<li><a href="#">Link 10</a></li>
<li><a href="#">Link 20</a></li>
<li><a href="#">Link 30</a></li>
<li><a href="#">Link 40</a></li>
</ul>
</li>
<li><a href="#">Link 3</a></li>
<li><a href="#">Link 4</a></li>
<li>
<span>Sub 2</span>
<ul>
<li><a href="#">Link 50</a></li>
<li><a href="#">Link 60</a></li>
<li>
<span>Sub 20</span>
<ul>
<li><a href="#">Link 100</a></li>
<li><a href="#">Link 200</a></li>
<li>
<span>Sub 3</span>
<ul>
<li><a href="#">Link 1000</a></li>
<li><a href="#">Link 2000</a></li>
<li><a href="#">Link 3000</a></li>
<li><a href="#">Link 4000</a></li>
</ul>
</li>
</li>
<li><a href="#">Link 400</a></li>
</ul>
</li>
<li><a href="#">Link 80</a></li>
</ul>
</li>
</ul>