Skip to main content

Різниця між visibility: hidden та display: none

display: none повністю видаляє елемент з потоку документа. visibility: hidden приховує його, але зберігає місце в макеті.

Теорія

TL;DR

  • display: none - як прибрати стілець з кімнати: інші меблі зрушуються на його місце
  • visibility: hidden - як накрити стілець невидимою тканиною: місце залишається порожнім
  • Головна різниця: чи займає елемент місце після того, як його приховано
  • Потрібна стабільність макету? visibility: hidden. Прибрати повністю? display: none
  • visibility можна анімувати разом з opacity; display не переходить взагалі

Швидкий приклад

html
<!-- display: none - Box 3 зсувається вгору --> <div class="container"> <div class="box">Box 1</div> <div class="box" style="display: none;">Box 2</div> <div class="box">Box 3</div> </div> <!-- visibility: hidden - Box 3 залишається на місці --> <div class="container"> <div class="box">Box 1</div> <div class="box" style="visibility: hidden;">Box 2</div> <div class="box">Box 3</div> </div>

У першому контейнері Box 3 зсувається туди, де був Box 2. У другому - стоїть точно на місці.

Головна різниця

display: none каже браузеру пропустити елемент під час побудови макету. Жодної блочної моделі не створюється: ні ширини, ні висоти, ні позиції. Сусідні елементи перераховують позиції так, ніби цього елемента не існує. visibility: hidden все одно проходить повний цикл побудови макету, отримує розраховані розміри, займає місце в потоці, але нічого не малює. Перемикання display запускає повний reflow. Перемикання visibility - лише repaint, що помітно дешевше.

Коли що використовувати

  • Без зсуву макету (підказки, hover-попапи, вирівняні дропдауни): visibility: hidden
  • Повне видалення з потоку (модальні вікна, умовні секції, динамічні списки): display: none
  • Плавна анімація появи: комбінуй visibility: hidden з opacity: 0
  • Видалення для скрін-рідерів і tab-порядку: display: none разом з aria-hidden="true"

Таблиця порівняння

Аспектdisplay: nonevisibility: hidden
Місце в макетіНі (інші переміщуються)Так (місце зберігається)
Блочна модельНе створюєтьсяСтворюється
АнімуєтьсяНіТак (з opacity)
Скрін-рідерПропускаєтьсяПропускається
Наслідується дітьмиНіТак (можна перевизначити)
Запускає reflowТакНі (лише repaint)
Коли використовуватиМодалки, collapse-меню, умовний рендерПідказки, hover-попапи, вирівнювання дропдаунів

Як браузер це обробляє

Движок Blink у Chrome пропускає елементи з display: none на фазі побудови макету. Вони не входять до контексту форматування, тому сусідні елементи розраховують позиції без них. Елементи з visibility: hidden проходять через макет нормально і отримують розраховані розміри. Фаза малювання просто пропускає їх відображення. Саме звідси різниця у reflow: одна властивість впливає на макет, інша - лише на малювання.

Типові помилки

Спроба анімувати display

css
.menu { transition: opacity 0.3s; /* це працює */ display: none; /* display не анімується */ }

display - дискретна властивість. Специфікація CSS Transitions не дозволяє переходити її, тому будь-який перехід на елементі з display: none ігнорується. Рішення - opacity + visibility:

css
.menu { opacity: 1; visibility: visible; transition: opacity 0.3s, visibility 0.3s; } .menu.hidden { opacity: 0; visibility: hidden; }

Забути про наслідування visibility

css
.parent { visibility: hidden; } /* всі дочірні елементи теж ховаються */ .child { visibility: visible; } /* це ПРАЦЮЄ - нащадок стає видимим */

На відміну від display: none, visibility можна перевизначити на нащадку. Якщо встановити visibility: visible на конкретному дочірньому елементі, він відображатиметься, навіть якщо батько прихований. Це зручно, коли треба показати один елемент всередині прихованої групи.

Залишати фокусовані елементи всередині visibility: hidden

Обидві властивості приховують контент від скрін-рідерів. Але visibility: hidden не видаляє елементи з tab-порядку. Прихована кнопка всередині visibility: hidden контейнера все одно доступна з клавіатури. display: none прибирає її з tab-порядку повністю. Для полів форм і кнопок, які треба повністю приховати, display: none безпечніший варіант.

Flexbox не стискується

css
.flex-container { display: flex; } .flex-child { flex: 1; } .flex-child.hidden { visibility: hidden; } /* займає свою частку flex-простору */

З visibility: hidden прихований елемент зберігає flex-простір, а сусідні не розширюються. З display: none сусідні розтягуються, щоб заповнити звільнений простір. Це плутає розробників при роботі з рівними колонками частіше, ніж здається.

Де зустрічається

  • React: display: none через умовний рендер ({isOpen && <Modal />}) для повного прибирання
  • Tailwind: клас hidden = display: none; клас invisible = visibility: hidden
  • Bootstrap: accordion collapse використовує display: none для вмісту панелей
  • Vue: v-show встановлює display: none; v-if повністю прибирає елемент з DOM
  • Бібліотеки підказок: visibility: hidden + opacity: 0 щоб уникнути зсуву макету при першому показі

Питання на співбесіді

Q: Що запускає reflow: перемикання display чи visibility?
A: Перемикання display запускає повний reflow. Перемикання visibility - лише repaint. Тому visibility дешевше для частих анімацій.

Q: Можна анімувати перехід між display: none і display: block?
A: Ні. display - дискретна властивість, її не можна переходити. Використовуй opacity і visibility разом.

Q: Чи може дочірній елемент перевизначити visibility: hidden батька?
A: Так. visibility: visible на нащадку зробить його видимим, навіть якщо батько прихований. З display: none так не вийде.

Q: У flex-контейнері: що станеться із сусідами, якщо один елемент отримає visibility: hidden?
A: Вони залишаться на місці. Прихований елемент зберігає flex-простір. З display: none сусіди б розтяглися.

Q: Що краще для підказки, яка часто перемикається?
A: visibility: hidden з opacity. Лише repaint при кожному перемиканні, без перерахунку макету. Саме цю комбінацію найчастіше зустрічаєш у виробничому коді для тултіпів - перехід залишається плавним навіть при швидких перемиканнях.

Приклади

Різниця у flex-рядку

html
<style> .row { display: flex; gap: 10px; margin-bottom: 20px; } .box { width: 100px; height: 100px; background: lightblue; display: flex; align-items: center; justify-content: center; } </style> <!-- display: none - проміжку немає --> <div class="row"> <div class="box">1</div> <div class="box" style="display: none;">2</div> <div class="box">3</div> </div> <!-- Результат: [1][3] --> <!-- visibility: hidden - проміжок залишається --> <div class="row"> <div class="box">1</div> <div class="box" style="visibility: hidden;">2</div> <div class="box">3</div> </div> <!-- Результат: [1][ ][3] -->

Box 3 стоїть на різних позиціях у кожному рядку. Саме ця різниця визначає, яку властивість обрати.

Плавна підказка з visibility + opacity

jsx
function Tooltip({ children, text }) { const [visible, setVisible] = useState(false); return ( <div style={{ position: 'relative', display: 'inline-block' }}> <button onMouseEnter={() => setVisible(true)} onMouseLeave={() => setVisible(false)} > {children} </button> <div style={{ position: 'absolute', top: '110%', left: 0, padding: '4px 8px', background: '#222', color: '#fff', borderRadius: 4, whiteSpace: 'nowrap', visibility: visible ? 'visible' : 'hidden', opacity: visible ? 1 : 0, transition: 'opacity 0.2s, visibility 0.2s', }}> {text} </div> </div> ); } // visibility: hidden зберігає розміри - позиція підказки стабільна при кожному показі // display: none перераховував би позицію заново, що дає мерехтіння при першому наведенні

Підказка стоїть точно на місці при кожному показі, бо її розміри завжди розраховані, просто не намальовані. Якщо змінити на display: none - при першому наведенні з'явиться помітне мерехтіння.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?