В коментарях до попереднього допису про display vs visibility ви ніби жартома згадали opacity. Чи не згадав я про нього незаслужено? Дивіться, цей спосіб вирізняється однією суттєвою деталлю: навіть будучи повністю "невидимим", а по факту просто прозорим, елемент повністю зберігає всю інтерактивність — фокусабельність, події клавіатури й миші, а ще залишається повністю "видимим" скрінрідерів.
І зі скрінрідерами така історія, що майже усі способи направду сховають елемент від них, окрім того самого opacity. З ним вам доведеться вручну додавати атрибут aria-hidden. Тому жарти жартами, а a11y в сучасному світі треба знати й практикувати.
А що зі швидкодією?
Ховаючи елемент, нам важливо знати дві речі: як він поводитиметься зі скрінрідерами та скільки "коштуватиме" його відновлення.
І на це впливає в першу чергу те, чи викликатиметься reflow — процес перерахунку макету з видимих елементів. Цей процес насправді надзвичайно легко викликати, достатньо змінити будь-яку властивість елемента, що відповідає за його "фізичну" поведінку: розміри чи відступи. Ну і, очевидно, display.
Не вдаючись в подробиці, зазначу, що цей процес тим затратніший, чим більше у вас елементів у render-tree. Однак при цьому не найзатратніший, бо при цьому не змінюється DOM.
А от visibility та opacity reflow не викликатимуть, бо вони не змінюють жодних "фізичних" параметрів елемента, тож їх застосування викличе лише repaint. А він, у свою чергу, достатньо швидкий і давно дуууже сильно оптимізований бравзерами.
Я відчуваю гостру необхідність зануритись разом із вами в процес відображення сторінки бравзером, а саме розібрати усі ці дерева, та як вони між собою повʼязані. Що скажете? 100 вподобайок вистачить?
DOM оновлювати дорого
А от якщо ви любите стріляти по горобцях з BFG, то, швидше за все, ви користуєтесь "умовним рендерингом" вашого улюбленого фреймворку чи бібліотеки. Чи це погано, запитаєте ви? It depends, відповім я.
На маленьких і нечастих рендерах то вже бог з ним, там втрата тої швидкодії фактично непомітна. А от коли ви таке практикуєте на частих оновленнях, або на великій кількості елементів, то ось час вас присоромити.
Вся справа в тому, що умовний рендеринг зазвичай просто видаляє/вставляє елементи з DOM. І попри усі намагання оптимізувати ці процеси, маніпуляції з DOM були, лишаються й будуть найдорожчими в світі.
Майже кожна, навіть найменша зміна в DOM (а, тим паче, видалення/додавання елемента) потребує перевиконання повного циклу:
1. Оновити DOM
1¾. Оновити CSSOM (якщо змінили інлайн-стилі)
2. Оновити render-tree
3. Виконати reflow
4. Виконати repaint
Знову ж таки, оптимізації, батчинг, та-та, чули, знаєм. Але усе одно оновлення DOM — дороге. І викликатиме дуууже помітні лаги, якщо ви одночасно ховаєте або показуєте багато елементів.
У мене колись давно в практиці була задача з кастомним багаторівневим акордеоном, і виявилося, що ховати секції з display: none набагато швидше, аніж з класичним conditional rendering. Так, лаги усе одно були, але значно менші.
Тож підібʼємо підсумок: не всі "хованки" однаково корисні.
display: none і умовний рендеринг справді прибирають елемент із рендер-дерева, але коштом повної перебудови макету, а conditional rendering так і взагалі за рахунок зміни структури DOM. visibility: hidden і opacity: 0 лишають елемент у потоці, але поводяться зовсім по-різному з погляду доступности та взаємодії. Перший — неактивний і “німіє”, другий — поводиться як повноцінний видимий елемент, просто невидимий.
І лише розуміючи ці відмінності, можна ефективно керувати поведінкою інтерфейсу, не жертвуючи швидкодією та доступністю.
***
Ви вже долучилися до збору на РЕБ? Усього 50 гривень і шанс виграти класні подарунки — ваш!