WTF, HTML и CSS?

Ниже перечислены причины, по которым HTML и CSS могут заставить вас чертыхнуться. Это список затруднений, ошибок и дилемм, которые в них присутствуют и стабильно портят нам настроение.

Содержание

  1. Объявление doctype
  2. Расчет размеров контейнера
  3. Единицы Rem и мобильный Safari
  4. Обтекаемые элементы идут первыми
  5. Обтекаемые элементы и клирфикс
  6. Обтекаемые элементы и вычисление высоты родителя
  7. Все обтекаемые элементы — блочные
  8. Смежные вертикальные отступы схлопываются
  9. Стилизация строк таблицы
  10. Firefox и кнопки с помощью тега <input>
  11. Внутреннее выделение у кнопок в Firefox
  12. Всегда устанавливайте type тега <button>
  13. Лимит на количество селекторов в Internet Explorer
  14. Объяснение того, как работает позиционирование
  15. Ширина элемента и его позиционирование
  16. Фиксированное позиционирование и свойство transform

Объявление doctype

Всегда нужно прописывать doctype. Я рекомендую простой doctype HTML5:

<!DOCTYPE html>

Его отсутствие может вызвать проблемы с таблицами, инпутами и т.д., если страница будет отображаться в режиме совместимости (quirks mode).

Расчет размеров контейнера

Элементы, для которых установлен width на самом деле шире, если у них есть padding и/или border-width. Чтобы этого избежать, используйте массово распространенный на сегодняшний день сброс box-sizing: border-box;.

Единицы Rem и мобильный Safari

В то время как мобильный Safari поддерживает использование rem в любом css-свойстве, при использовании rem в медиа-запросах происходит конфуз и он начинает бесконечно менять размеры текста.

На данный момент вместо rem стоит использовать em.

html {
  font-size: 16px;
}

/* Вызывает баг с изменением размеров Mobile Safari */
@media (min-width: 40rem) {
  html {
    font-size: 20px;
  }
}

/* В Mobile Safari все отлично работает */
@media (min-width: 40em) {
  html {
    font-size: 20px;
  }
}

Нужна помощь Если у вас есть ссылка на баг-репорт в Apple или WebKit, я с удовольствием его добавлю. Сам я не уверен куда слать баг-репорт, так как этот баг воспроизводится только на мобильном Safari.

Обтекаемые элементы идут первыми

Элементы, к которым применен float, всегда должны отображаться в документе первыми. Элементы надо обернуть в какой-то блок, иначе может возникнуть эффект лесенки, когда контент показывают сбоку от обтекаемого элемента, а не под ним.

<div class="parent">
  <div class="float">Float</div>
  <div class="content">
    <!-- ... -->
  </div>
</div>

Обтекаемые элементы и клирфикс

Если вы применили к элементу float, то, вероятно, вы захотите его заклирить. Любой контент, который идет после такого элемента будет его обтекать, если к элементу не применен клирфикс. Для создания клирфикса можно использовать техники, приведенные ниже.

Для клирфикса, вызываемого отдельным классом можно использовать микро клирфикс.

.clearfix:before,
.clearfix:after {
    display: table;
    content: "";
}
.clearfix:after {
    clear: both;
}    

В качестве альтернативы можно задать родителю overflow, со значениями auto или hidden.

.parent {
    overflow: auto; /* clearfix */
}
.other-parent {
    overflow: hidden; /* clearfix */
}

Имейте ввиду, что overflow может привести к появлению нежелательных побочных эффектов, обычно касающихся элементов, для которых установлено позиционирование, отличное от static.

Подсказка Сделайте себе и своим сотрудникам одолжение, добавляя комментарии вроде /* clearfix */, когда клирите обтекаемые элементы, так как свойства могут использоваться там и по совершенно другим причинам.

Обтекаемые элементы и вычисление высоты родителя

Высота родительского элемента, для всех дочерних элементов которого установлен float будет равна 0. Чтобы высота соответствовала реальной следует использовать clearfix.

Все обтекаемые элементы — блочные

Элементы с установленным float автоматически получают display: block;. Поэтому не имеет смысла устанавливать и то и другое, так как float все равно перезапишет установленное вами значение display.

.element {
  float: left;
  display: block; /* Не обязательно */
}

Забавный факт Много лет назад мы должны были устанавливать display: inline; для того, чтобы избежать бага с двойными отступами в IE6. Однако, эти дни давно в прошлом.

Смежные вертикальные отступы схлопываются

Верхний и нижний margin соседних элементов (когда один идет сразу за другим) могут и будут схлопываться, но это не касается элементов с float или тех, которые позиционированы абсолютно. Подробнее об этом вы можете прочесть в этой статье в MDN или разделе о схлопывании margin в спецификации CSS2.

Смежные горизонтальные margin никогда не схлопываются.

Стилизация строк таблицы

Для строк таблицы, тегов <tr>, нельзя задать border, если не задан border-collapse: collapse; на родительском элементе <table>. Более того, если у дочерних тегов <td> или <th> установлено то же значение border-width, что и для <tr>, то к <tr> border не применится. Можете убедиться в этом на примере.

Firefox и кнопки с помощью тега <input>

По загадочным причинам Firefox применяет к тегу input с типами submit и button line-height, которое не удастся перезаписать с помощью CSS. Есть два решения этой проблемы:

  1. Использовать элемент <button>
  2. Не использовать в кнопках line-height

Если вы предпочли первый вариант (и я бы рекомендовал именно его, так как тег <button> просто отличный), вот что нужно знать:

<!-- Плохая идея -->
<input type="submit" value="Сохранить изменения">
<input type="button" value="Отменить">
 
<!-- Отлично работает везде -->
<button type="submit">Сохранить изменения</button>
<button type="button">Отменить</button>

Если вы захотите выбрать второй, просто не устанавливайте line-height и используйте для вертикального выравнивания текста кнопки только padding. Откройте пример в Firefox, чтобы увидеть, в чем проблема и как с ней справится.

Хорошие новости Похоже в 30-й версии Firefox ошибку исправят. Но это хорошая новость для нас в будущем, а пока не забывайте исправлять этот баг в CSS.

Внутреннее выделение у кнопок в Firefox

Firefox добавляет внутреннее выделение в кнопках (и в <input>, и в <button>) для :focus. Вероятно, это делается для повышения доступности, но расположение весьма странное. Чтобы избавиться от этого безобразия можно использовать следующий CSS:

input::-moz-focus-inner,
button::-moz-focus-inner {
    padding: 0;
    border: 0;
}

Можете увидеть как это работает в том же примере, что и в предыдущем разделе.

Подсказка Добавляйте какое-то состояние для buttons, links, и inputs, находящимся в фокусе. Обеспечение очевидности и доступности интерфейсов это первостепенная задача, важная как для продвинутых пользователей, которые быстро пробегают контент глазами, так и для пользователей с плохим зрением.

Всегда устанавливайте type тега <button>

Значение по умолчанию атрибута type — submit, что значит, что любая кнопка в форме может её отправить. Следует использовать type="button" для любых кнопок, которые этого делать не должны и явно определять type="submit" для остальных.

<button type="submit">Сохранить изменения</button>
<button type="button">Отменить</button>

Для действий, которые требуют использования тега <button> не находящегося в форме используйте type="button".

<button class="dismiss" type="button">x</button>

Забавный факт: IE7 явно не корректно поддерживает атрибут value тега <button>. Вместо того, чтобы прочесть значение атрибута, он получает его из innerHTML (содержимое между открывающим и закрывающим тегом <button>). Но, по двум причинам, я не вижу в этом проблемы: IE7 уже почти не используется и это достаточно странная ситуация, когда одновременно заданы и значение атрибута value и контент внутри <button>.

Лимит на количество селекторов в Internet Explorer

Internet Explorer 9 и ниже обладает ограничением на 4,096 селекторов в таблице стилей. Кроме того существует лимит в 31 таблицу стилей и <style></style> подключенных на одной странице. Все таблицы сверх этого лимита просто игнорируются браузером. Или разбивайте ваш CSS или начинайте рефакторить. Я бы предложил последнее.

В качестве полезного дополнения, вот описание того, как браузеры считают селекторы:

/* Один селектор */
.element { }
 
/* Ещё два селектора */
.element,
.other-element { }
 
/* Ещё три селектора */
input[type="text"],
.form-control,
.form-group > input { }

Объяснение того, как работает позиционирование

Элементы с position: fixed; позиционируются относительно вьюпорта браузера. Элементы с position: absolute; позиционируются относительно ближайшего родителя со значением position, отличным от static (например, relative, absolute, или fixed)

Ширина элемента и его позиционирование

Не стоит задавать width: 100%; для элементов с position: [absolute|fixed];, left, и right. Использование width: 100%; работает так же как совместное использование left: 0; и right: 0;. Стоит использовать либо то, либо другое, но не все вместе.

Фиксированное позиционирование и свойство transform

Браузеры игнорируют position: fixed; если у родителя элемента установлено свойство transform. Использование трансформаций создает новый контейнер, в результате чего элемент принудительно оказывается в блоке с position: relative; и элемент с фиксированным позиционированием начинает вести себя как элемент с position: absolute;.

Посмотрите пример и прочтите пост Эрика Мэйера (Eric Meyer) на эту тему.