Плавное изменение высоты блока без jQuery
В данной практической статье рассмотрим, как плавно изменять высоту блока при отображении и скрытии, если конечная высота блока не известна
К сожалению, корректного решения данной задачи c помощью CSS нет, так как значение auto
для высоты не может быть анимировано с помощью свойства transition
В библиотеке jQuery для этого есть специальные методы slideDown()
, slideUp()
, slideToggle()
, но подключать целую библиотеку не всегда есть смысл или возможность
Сделаем простой вариант решения для этой задачи
Результат можно сразу посмотреть на Codepen или прямо в статье чуть ниже
Верстаем основу
Сначала сделаем структуру HTML по методологии БЭМ
Элемент <div class="item__content">
плавно будет менять высоту, чуть ниже мы зададим ему несколько обязательных CSS-свойств.
Обратите внимание, что сам контент должен находится в дочернем/вложенном блоке, в данном случае в <div class="item__body">
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Smooth Height</title>
<link rel="stylesheet" href="css/bootstrap-reboot.min.css"/>
<link rel="stylesheet" href="css/main.css"/>
</head>
<body>
<section class="section">
<div class="container">
<div class="section__item item">
<div class="item__button">Lorem ipsum dolor sit amet, consectetur adipisicing.</div>
<div class="item__content"> <!-- этот элемент будет плавно менять высоту -->
<div class="item__body">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repellendus voluptatum minus ipsum quas, eum ad inventore perferendis maiores distinctio nisi maxime sunt numquam veniam, tempore. Id error deleniti, odio atque.</div>
</div>
</div>
<div class="section__item item">
<div class="item__button">Lorem ipsum dolor sit amet, consectetur adipisicing.</div>
<div class="item__content"> <!-- этот элемент будет плавно менять высоту -->
<div class="item__body">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repellendus voluptatum minus ipsum quas, eum ad inventore perferendis maiores distinctio nisi maxime sunt numquam veniam, tempore. Id error deleniti, odio atque.Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repellendus voluptatum minus ipsum quas, eum ad inventore perferendis maiores distinctio nisi maxime sunt numquam veniam, tempore. Id error deleniti, odio atque.</div>
</div>
</div>
</div>
</section>
<script src="js/main.js"></script>
</body>
</html>
Добавим CSS стили
Воспользуемся CSS-переменными для управления цветом
Активное состояние элемента будем определять через data-атрибут data-open
.
Если значение data-атрибута data-open
будет равно true
, селектор активного элемента будет выглядеть следующим образом - .section__item[data-open="true"]
Чтобы элемент .item__content
мог плавно менять высоту, ему необходимо задать несколько обязательных CSS-свойств
.item__content {
max-height: 0; /* изначально блок свёрнут */
overflow: hidden; /* полностью скрывает дочерний блок */
transition: max-height 0.9s; /* время анимации можно менять по желанию */
}
Все CSS стили для HTML структуры, описанной выше
:root {
--main: #6d6de4;
--main-darken: var(--main);
}
body {
background: #121212;
color: #fff;
}
.section {
padding: 60px 0;
}
.section__item[data-open="true"] {
--main-darken: #4c4cdd;
}
.section__item:not(:last-child) {
margin-bottom: 30px;
}
.container {
max-width: 1140px;
padding: 0 30px;
margin: 0 auto;
}
.item {
overflow: hidden;
border-radius: 30px;
}
.item__button {
font-weight: bold;
padding: 15px 30px;
display: flex;
align-items: center;
min-height: 60px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
cursor: pointer;
background: var(--main-darken);
transition: 0.6s;
}
.item__content {
max-height: 0;
overflow: hidden;
transition: max-height 0.9s;
}
.item__body {
padding: 30px;
background: var(--main);
transition: 0.6s;
line-height: 1.8;
font-size: 16px;
}
Пишем логику на JavaScript
Кратко опишу логику кода.
При клике на кнопку/заголовок элемента - проверяем, активен ли элемент. Если не активен, то делаем его активным, и добавляем inline-свойство max-height
для блока контента со значением, равной значению его полной высоты scrollHeight
. Так как теперь высота блока с контентом известна, то свойство transition
работает в обычном режиме и постепенно увеличивает max-height
до установленного значения.
При повторном нажатии на кнопку/заголовок, сбрасываем ранее установленное inline-свойство max-height
, и блок с контентом сворачивается к своему начальному нулевому значению, указанном в CSS
При изменении размеров окна, проверяем - если элемент активен и текущее значение max-height
отличается от полной высоты блока с контентом, то корректируем значение max-height
Код с комментариями
document.addEventListener('DOMContentLoaded', () => { // Структура страницы загружена
const smoothHeight = (itemSelector, buttonSelector, contentSelector) => { // объявляем основную функцию, которая принимает в качестве параметров селекторы элемента, кнопки внутри элемента и блока с контентом
const items = document.querySelectorAll(itemSelector) // находим все элементы по переданному селектору в параметре itemSelector и записываем в константу items
if (!items.length) return // если таких элементов нет, прекращаем выполнение функции
items.forEach(el => { // для каждого элемента
const button = el.querySelector(buttonSelector) // находим кнопку и записываем в константу button
const content = el.querySelector(contentSelector) // находим блок с контентом и записываем в константу content
button.addEventListener('click', () => { // при клике на кнопку
if (el.dataset.open !== 'true') { // если значение data-атрибута open у элемента не равно 'true' и блок с контентом еще не отображен
el.dataset.open = 'true' // тогда устанавливаем значение 'true'
content.style.maxHeight = `${content.scrollHeight}px` // и блоку с контентом устанавливаем inline-свойсво max-height со вычисленным значением полной высоты этого блока
} else { // если блок с контентом отображен и значение data-атрибута open у элемента равно 'true'
el.dataset.open = 'false' // тогда устанавливаем значение 'false'
content.style.maxHeight = '' // и сбрасываем ранее установленное inline-свойсво max-height
}
})
const onResize = () => { // объявляем функцию onResize, которая будет корректировать значение inline-свойства max-height при изменении размеров окна браузера
if (el.dataset.open === 'true') { // если значение data-атрибута open у элемента равно 'true' (коректировать высоту нужно только если блок контента отображен)
if (parseInt(content.style.maxHeight) !== content.scrollHeight) { // если текущее значение inline-свойства max-height у блока контента не равно полной высоте
content.style.maxHeight = `${content.scrollHeight}px` // только тогда блоку с контентом корректируем значение inline-свойсва max-height
}
}
}
window.addEventListener('resize', onResize) // вызываем функцию onResize при изменении размеров окна браузера
})
}
smoothHeight('.section__item', '.item__button', '.item__content') // вызываем основную функцию smoothHeight и передаем в качестве параметров необходимые селекторы
})
Получаем следующий результат - элементы активны
Итоги
Данный способ не универсальный, но поняв логику, вы можете добалять/изменять функциональность этого примера для конкретной задачи
Буду рад, если статья оказалась полезной
Спасибо за ваше внимание и уделённое время!
Полезные ссылки
Использование data-* атрибутов