Нумо рефакторити. Семантика та легкість сприйняття HTML

Anna Prykhodko
4 min readAug 17, 2021

--

Photo by Greg Rakozy on Unsplash

У сьогоднішньому прикладі я хочу провести рефакторинг фрагмента HTML, а саме — верхнього меню сайту. Для чистоти прикладу я видалила повторювані елементи та трохи очистила нерелевантні поточному завданню атрибути.

Disclaimer. Приклади для рефакторингу я беру із реальних джерел (сайтів, open-source проєктів, статей), змінюючи назви сутностей або інших унікальних компонентів. Я не знаю і не можу знати контекст, умови або час написання коду, тому всі мої виправлення є моїм особистим суб’єктивним сприйняттям того, що можна змінити в коді. Тому мої правки не містять жодної негативної оцінки першоджерела чи його творців.

Першоджерело

<div class="nav-links">
<div class="UITabs -with-icons">
<a class="UITabs-tab" href="/map" data-tab="map">
<div class="UITabs-content">
<div class="UITabs-icon">
<svg
class="ico" width="20" height="20"
viewBox="0 0 20 20" fill="#9e9e9e"
xmlns="http://www.w3.org/2000/svg"
>
<path fill-rule="evenodd" clip-rule="evenodd" d="...">
</path>
</svg>
</div>
Map
</div>
</a>
<a class="UITabs-tab" href="/discount" data-tab="discount">
<div class="UITabs-content">
<div class="UITabs-icon">
<svg
class="ico" width="20" height="20"
viewBox="0 0 20 20" fill="#9e9e9e"
xmlns="http://www.w3.org/2000/svg"
>
<path fill-rule="evenodd" clip-rule="evenodd" d="...">
</path>
</svg>
</div>
Discount
</div>
</a>
<div class="UITabs-tab" data-tab="search">
<div class="UITabs-content">
<div class="UITabs-icon">
<svg
class="ico" fill="#9e9e9e" height="24"
viewBox="0 0 24 24" width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path d="..."></path>
<path d="M0 0h24v24H0z" fill="none"></path>
</svg>
</div>
Search
</div>
</div>
<div class="UITabs-indicator" style="transform..."></div>
</div>
</div>

Перше, на що я звернула увагу, це велика кількість тегу <div>. Нічого поганого в цьому тезі немає, але, як і все добре, він використовується занадто часто.

Наступне — кількість вкладень, які явно не несуть ніякого семантичного змісту. Глибокі вкладення роздувають розмір HTML-файлу, збільшують час парсингу CSS та загально погіршують читабельність.

Також — іконки, які знаходяться в HTML. Це теж не критично, але зберігання іконок залежить від стратегії кешування, наявності CDN, списку підтримуваних браузерів і т.д. У даному випадку, іконки не будуть завантажуватися окремо від HTML-файлу, а отже будуть відображені одразу, без стрибків контенту, але збільшать розмір файлу, та сповільнять його завантаження та парсинг. Очевидно, не буває однозначно правильного або неправильного рішення в цьому питанні, але з точки зору семантики, на мій погляд, іконки забруднюють код, вони важливі тільки з візуальної точки зору, на відміну від зображень. Візуальну частину сторінки краще винести в CSS.

Давайте підберемо інші семантично вірні теги, зменшимо кількість вкладень та очистимо від зайвих елементів цей код. Підемо по порядку.

Контейнер

Оскільки це меню, більш доцільним для контейнера буде тег <nav>, який використовується для елементів навігації.

Тепер питання — навіщо нам два вкладених один в одного теги <div>? Схоже, що .nav-links містить стилі позиціонування щодо свого батьківського елемента, в той же час .UITabs — справжній контейнер навігації. Я не бачу сенсу розділяти стилі на два різних теги, тому пропоную замінити їх на один тег <nav> з двома класами.

<nav class="UITabs -with-icons nav-links">
<a .../>
<a .../> <div .../>

<div .../>
</nav>

Пункти меню

Питань до тегів посилань немає, але знову присутня зайва вкладеність, клас .UITabs-content. І знову це розділення тегів за їх стилями, але в цьому випадку, ймовірно, це спроба дотримуватись методології типу BEM. Немає сенсу відокремлювати стилі блоку .UITabs-tab від його єдиного прямого нащадка – .UITabs-content. Тим більше, в обох класах встановлюються відступи, перевизначаючи один одного. Як і в разі контейнера, пропоную об’єднати ці два теги в один.

<a class="UITabs-tab" href="/map" data-tab="map">
<div class="UITabs-icon">...</div>
Map
</a>

Крім посилань, пунктом меню є <div>, що невірно з семантичної точки зору, так як пункт меню за визначенням є елементом взаємодії користувача, тобто повинен бути або посиланням, або кнопкою. У нашому випадку це кнопка пошуку, отже так і запишемо.

<button class="UITabs-tab" data-tab="search">
<div class="UITabs-icon">...</div>
Search
</button>

Іконки

Як я вже писала вище, я пропоную винести іконки з HTML, їм краще місце в CSS, а з урахуванням того, що їх не повинно бути зовсім, вони будуть встановлені в псевдоелементі.

<style>
.UITabs-tab.-map-icon::before {
content: url(/map-icon.svg); //first option
}
.UITabs-tab.-discount-icon::before {
content: url(data:image/svg+xml; utf8, <svg>...</svg>); //second
}
...
</style>
<a class="UITabs-tab -map-icon" href="/map" data-tab="map">
Map
</a>

Інші елементи

Останній пункт, який викликає питання, є .UITabs-indicator. Фактично, це підкреслення активного пункту меню, до якого застосовуються стилі за допомогою скрипту. Мені здається, це зайва ускладненість, враховуючи, що підкреслення можна легко реалізувати за допомогою властивості border в CSS. Або, якщо форма підкреслення нестандартна, можна використовувати псевдоелемент ::after або властивість box-shadow для стилізації.

<style>
.UITabs-tab.active {
border-bottom: 4px solid currentColor;
}
</style>

Результат

<style>
.UITabs-tab.-map-icon::before {
content: url(/map-icon.svg);
}
.UITabs-tab.-discount-icon::before {
content: url(data:image/svg+xml; utf8, <svg>...</svg>);
}
.UITabs-tab.-search-icon::before {
content: url(data:image/svg+xml; utf8, <svg>...</svg>);
}
.UITabs-tab.active {
border-bottom: 3px solid currentColor;
}
...
</style>
<nav class="UITabs -with-icons nav-links">
<a class="UITabs-tab -map-icon" href="/map" data-tab="map">
Map
</a>
<a class="UITabs-tab -discount-icon" href="/discount" data-tab="discount">
Discount
</a>
<button class="UITabs-tab -search-icon" data-tab="search">
Search
</button>
</nav>

На мій погляд, такий HTML є більш зрозумілим та семантично правильним. До речі, я не використовувала <div>, хоча це й не було моєю метою, але так часто виходить, якщо дотримуватися семантики.

Дякую за те, що дочитали!
Побачимось наступного разу! 🤹🏻‍♀️

--

--

Anna Prykhodko
Anna Prykhodko

Written by Anna Prykhodko

Front-end developer · Kyiv 🇺🇦

No responses yet