en.javascript.info/02-ui/05-widgets/03-widgets-markup/article.md
Ilya Kantor f301cb744d init
2014-10-26 22:10:13 +03:00

193 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Вёрстка графических компонентов
При создании графических компонент ("виджетов") в первую очередь придумывается их HTML/CSS-структура.
Как будет выглядеть виджет в обычном состоянии? Как будет меняться в процессе взаимодействия с посетителем?
Чтобы разработка виджета была удобной, при вёрстке полезно соблюдать несколько простых, но очень важных соглашений.
[cut]
## Семантическая вёрстка
**HTML-разметка и названия CSS-классов должны отражать не оформление, а смысл.**
Например, сообщение об ошибке можно сверстать так:
```html
<div *!*style="color:red; border: 1px solid red"*/!*>
Плохая вёрстка сообщения об ошибке: атрибут style!
</div>
```
...Или так:
```html
<div *!*class="red red-border"*/!*>
Плохая вёрстка сообщения об ошибке: несемантический class!
</div>
```
В обоих случаях вёрстка не является семантической. В первом случае -- стиль, а во втором -- класс содержат информацию об *оформлении*.
**При семантической вёрстке классы описывают смысл ("что это?" -- меню, кнопка...) и состояние (открыто, закрыто, отключено...) компонента.**
Например:
```html
<div *!*class="error"*/!*>
Сообщение об ошибке (error), правильная вёрстка!
</div>
```
У предупреждения будет класс `message` и так далее, по смыслу.
```html
<div *!*class="warning"*/!*>
Предупреждение (warning), правильная вёрстка!
</div>
```
**Семантическая верстка упрощает поддержку и развитие CSS, упрощает взаимодействие между членами команды.**
Такая верстка удобна для организации JS-кода. В коде мы просто ставим нужный класс, остальное делает CSS.
## Состояние виджета -- класс на элементе
Зачастую компонент может иметь несколько состояний. Например, меню может быть открыто или закрыто.
**Состояние должно добавляться CSS-классом не на тот элемент, который нужно скрыть/показать/..., а на тот, к которому оно "по смыслу" относится, обычно -- на корневой элемент.**
Например, меню в закрытом состоянии скрывает свой список элементов. Класс `open` нужно добавлять не к списку опций `<ul>`, который скрывается-показывается, а к *корневому элементу* виджета, поскольку это состояние касается всего меню:
```html
<div class="menu *!*open*/!*">
<span class="title">Заголовок меню</span>
<ul>
<li>Список элементов</li>
</ul>
</div>
```
Ещё пример -- индикатор загрузки:
```html
<div class="loader">
<span class="progress">Тут показывается прогресс</span>
</div>
```
Состояние индикатора может быть "в процессе" (loading) или "загрузка завершена" (complete). С точки зрения оформления оно может влиять только на показ внутреннего `span`, но ставить его нужно всё равно на внешний элемент, ведь это -- состояние всего компонента:
```html
<div class="loader *!*loading*/!*">
<span class="progress">Тут показывается прогресс</span>
</div>
```
Из примеров выше можно подумать, что классы, описывающие состояние, всегда ставятся на корневой элемент. Но это не так.
Возможно и такое, что состояние относится к внутреннему элементу. Например, для дерева состояние открыт/закрыт относится к узлу, соответственно, класс должен быть на узле.
Например:
```html
<ul class="tree">
<li class="*!*closed*/!*">
Закрытый узел дерева
</li>
<li class="*!*open*/!*">
Открытый узел дерева
</li>
...
</ul>
```
На практике, даже если в начале разработки поставить класс не там -- то, при правильном понимании CSS, рано или поздно он всё равно переместится куда надо, поскольку стилизация открытого/закрытого меню касается также и заголовка.
Но оптимальнее -- сразу ставить его на правильное место.
## Префиксы у классов
Посмотрите, пожалуйста, вёрстку для виджета диалогового окна.
```html
<div class="dialog">
<h2 class="title">Заголовок</h2>
<div class="content">
Содержимое. Имена классов в этой вёрстке опасны.
</div>
</div>
<style>
.dialog .title { стиль заголовка }
.dialog .content { стиль содержимого окна }
</style>
```
В этой вёрстке есть серьёзная проблема, которая появится, если в содержимом окна будет элемент с классом `.title`:
```html
<div class="dialog">
<h1 class="*!*title*/!*">Заголовок</h1>
<div class="content">
*!*
<h2 class="*!*title*/!*">Привет!</h2>
... текст диалога ...
*/!*
</div>
</div>
```
Такое вполне возможно, ведь диалоговое окно может иметь любое содержимое.
**В этом случае CSS-правило `.dialog .title` будет применено и к `<h2 class="title">` в содержимом с непредсказуемыми последствиями.**
Конечно, можно попытаться бороться с этим. Например, нейтрализовать его действие, добавив дополнительное правило `.dialog .content .title`, но это скорее "заплатка", нежели полноценное решение проблемы.
Ещё один вариант -- жёстко задать вложенность. А именно, использовать класс `.dialog > .title`. Это сработает в данном конкретном примере, но как быть в тех местах, где нужен более глубокий потомок?
**Чтобы избежать возможных проблем, все классы внутри виджета начинают с его имени.**
Подходящий вариант:
```html
<div class="*!*dialog*/!*">
<h1 class="*!*dialog-title*/!*">Заголовок</h1>
<div class="*!*dialog-content*/!*">Содержимое</div>
</div>
<style>
.dialog-title { стиль загловка }
.dialog-content { стиль содержимого окна }
</style>
```
В этом случае внутрь `.dialog-content` можно смело помещать другие компоненты со своими классами `..-title`, `..-content` и т.п.
Кроме всего прочего, обработка такого CSS будет чуть-чуть быстрее ;) Так как один класс вместо каскада.
[smart header="Когда префиксы не нужны?"]
Префиксы делают названия классов длиннее, поэтому иногда не хочется их ставить.
Без них можно обойтись в тех случаях, когда внутри элемента заведомо не будет произвольного HTML и других компонент. То есть когда конфликты заведомо исключены.
С другой стороны, требования имеют свойство расти. Компоненты зачастую вставляются туда, где их не предполагалось. Ваш виджет, написанный для одной задачи или проекта, может быть потом использован совсем в другом месте, где потребуются вложенные компоненты. И тогда заранее предусмотренные префиксы сослужат хорошую службу.
[/smart]
## Итого
<ul>
<li>Вёрстка должна быть семантической, использовать соответствующие смыслу информации теги и классы.</li>
<li>Класс, описывающий состояние всего компонента, нужно ставить на его корневом элементе, а не на том, который нужно "украсить" в этом состоянии. Если состояние относится не ко всему компоненту, а к его части -- то на соответствующем "по смыслу" DOM-узле.</li>
<li>Классы внутри компонента должны начинаться с префикса -- имени компонента.
Это не всегда строго необходимо, но позволяет избежать проблем в случаях, когда компонент может содержать произвольный DOM, как например диалоговое окно с произвольным HTML-текстом.
Использование `.dialog-title` вместо `.dialog .title` гарантирует, что CSS не применится по ошибке к какому-нибудь другому `.title` внутри диалога.
</li>
</ul>