Непростая стилизация строк таблицы и непонятный баг в IE9
Несколько дней назад, у одной моей знакомой верстальщицы возникла задачка по вёрстке, которая касалась необычного раскрашивания строк таблицы при наведении. Во время того, как рассматривались всякие решения, был выявлен странный баг в IE9.
В общем, в этом посте я хочу поделиться найдненными решениями (а возможно узнать и лучше), а заодно и непонятным багом, в надежде что кто-нибудь из заинтересовавшихся комментаторов поможет мне понять его природу.
Начнём с задачи
В принципе, условие задачи очень простое. Есть таблица, состоящая из n-количества строк. При наведении указателем мыши на строку она должна изменять цвет, получать одна-пиксельную границу, а вдобавок ещё и тень. Что касается поддержки браузеров: IE8+, но в IE8 тени может и не быть. Думаю на рисунке ниже всё станет понятно.
Рассуждения вслух
Казалось бы, что может быть проще повесить все стили на сами строки и не париться. Но не всё так-то легко, как кажется. Дело в том, что table-row — непростой бокс, собственно как и сама таблица, поэтому и стилизация строк в браузерах очень хромает. Связываться в этом плане со стоками, мягко говоря… рискованное занятие, и поведение Opera на это недвусмысленно намекает, применяя тень к отдельным ячейкам.
Так как поддержка IE6-7 нам не нужна, мы смело может воспользоваться псевдоэлементами. И первое что пришло в мне в голову, это повесить псевдоэлементы на каждую строку, задав им следующие стили (для примера я задал стили только верхней строке):
table tr:first-child:before { content: ''; position: absolute; left: 0px; height: 31px; width: 100%; border: 1px solid #47a9e1; box-shadow: 0 0 4px #47a9e1; }
Фактически всю ответственность за стилизацию строки я переложил на псевдоэлемент :before, рассчитывая, что это и будет решением задачи. Но как оказалось, я ошибался.
Вместо того, чтобы просто сгенерировать псевдоэлемент внутри строки и растянуть его на 100% по ширине, в таблице появилась и дополнительная ячейка! Да, вы не ослышались, строка стала больше ровно на две сущности вместо одной:) Вот это чудеса, подумал я, и позвал на помощь своего коллегу Илью Стрельцына.
Илья объяснил мне, что любая нежданная сущность внутри tr, но снаружи td, оборачивается в анонимный table-cell. И неважно, абсолютно позиционированная ли это сущность или нет. Рендереру таблиц всё равно. В нашем случае абсолютно отпозиционированный блочный бокс, который мы видим растянутым на всю ширину таблицы, оказался всё равно внутри «виртуальной» пустой ячейки, присутствие которой мы наблюдаем в первой строке таблицы. Это в общем-то стандартное поведение табличной модели в CSS, в скором будущем мы раскроем эту тему подробнее:)
Ради интереса стоит заменить, что, если бы я поменял :before на :after, то картина бы стала приятнее. Но это не избавило бы нас от ответственности. Ведь, по сути, здесь нам просто повезло, что анонимный table-cell появился в конце строки, а не вначале. Но это же совсем не означает, что его вовсе нет, или что при каких-нибудь манипуляциях с ячейками он бы не проявил себя во всей красе.
Отсюда можно сделать вывод, что не стоит добавлять псевдоэлементы к tr, если явно не планируется ставить им display : table-cell;
Баг в IE9
Перед тем, как переходить к основному решению, давайте рассмотрим один интересный баг в браузере IE9.
После перебора всего вышесказанного я стал искать решение дальше, и подумал, а почему бы мне не засунуть какой-нибудь элемент в первую ячейку строки и не сделать наш бокс с тенью именно из него? Сказано — сделано.
<tr> <td><i class="box-shadow"></i> 07/08/12 11:08 AM</td> <td>Voicemail Transcription A328413</td> <td>IN PROGRESS</td> <td>NA</td> <td>NA</td> </tr>
table tr .box-shadow { position: absolute; left: 0px; margin-top: -10px; height: 31px; width: 100%; border: 1px solid #47a9e1; box-shadow: 0 0 4px #47a9e1; pointer-events: none; }
И этот приём сработал… но только не в IE9. В нём ни в какую не хотела отображаться тень. Как я только не двигал координаты бокса и не игрался с его стилями, победить IE9 мне так и не посчастливилось:(
Методом тыка удалось обнаружить, что если убрать border-collapse у таблицы, то тень в IE9 появляется! Простите, но ведь это же бред и маразм?! Где же логика? Скорее всего ответ кроется в том, что IE через одно место наследует боксовую модель от ячейки, которая при collapse сама становится некроссбраузерной и непредсказуемой.
Поэтому мне ничего не оставалось делать, как продолжить искать решение дальше. Но я не пожалел об этом, потому что я его нашёл и оно оказалось ещё проще:)
Решение
Псевдоэлементы, которые ранее давали сбой у строк, отлично подошли к ячейкам таблицы. Короче говоря, я решил заменить элемент внутри первой ячейки строк на псевдоэлемент для самой ячейки. Выкладываю сразу весь рабочий код:
<table class="table"> <tbody> <tr> <td> 07/08/12 11:08 AM</td> <td>Voicemail Transcription A328413</td> <td>IN PROGRESS</td> <td>NA</td> <td>NA</td> </tr> .... </tbody> </table>
table { background: #F8F8F8; color: #666; font-family: Arial, Helvetica, sans-serif; font-size: 12px; margin: 0 auto; border-collapse: collapse; border-spacing: 0; line-height: 1; position: relative; width: 600px; } table tr:hover td:first-child:before { content: ''; position: absolute; left: 0px; margin-top: -10px; height: 31px; width: 100%; border: 1px solid #47a9e1; box-shadow: 0 0 4px #47a9e1; pointer-events: none; } table tr:hover { background: #d1e9f7; } table tr { border: 1px solid #999; } table td, table th { padding: 10px; }
И сам результат.
Теперь всё работает так, как нам нужно. И самое удивительное, что это касается и IE9, который ранее почему-то отказывался работать с обычным элементом, но, в данной ситуации, прекрасно повёл себя с псевдоэлементом и стал отображать тень, как и другие браузеры. Чудеса? А кто его знает:) Но скорее всего дело в том, что в отличие от обычного элемента псевдоэлемент не имеет наследственных болезней от наследуемой боксовой модели, т.к. создаётся всегда с нуля. Поэтому и проблем в IE9 с ним в этом плане нет.
Заключение
В завершении этого поста, мне хочется спросить у народа, может у кого-то есть решение получше? Особенно интересует универсальное решение, для резиновой по высоте ячеек таблицы.
Так же буду признателен, если кто-нибудь объяснит мне точную природу бага в IE9.
P.S. Это тоже может быть интересно:
А если использовать display: table, table-row, table-cell вместо обычной таблицы ?
Табличные данные должны быть в таблице. Ради красивой подсветки строки таблицы при навдении нарушать семантику кода считаю нелогичной.
Так CSS-боксы будут теми же самыми. С теми же ограничениями (плюс свои дополнительные, типа невозможности colspan/rowspan). В чем профит?
Если вы нашли подходящее вам решение, но уперлись в border-collapse: collapse, то может стоит тогдла отказаться от collapse?
А бордюр рисовать ячейкам так: border-right и border-bottom. Первой строке и первому столбцу при помощи first-child`а дорисовать оставшиеся.
Основная ирония момента в том, что решение, которое требует отсутствия collapse (с дополнительной разметкой), всё равно проигрывает решению, не зависящему от него (без лишнего, на псевдоэлементах). Обычно бывает наоборот («дубово, но надежно» vs. «изящно, но с кучей оговорок»), а здесь проблема появилась как раз у «дубового» (на первый взгляд) решения.
Вариант с выборочными бордерами ячеек интересен (по крайней мере, когда не нужны вертикальные разделители). Но как быть с тенью?
В ие8 кстати, на ховер срабатывает только заливка цветом на tr — бордеры отсутсвуют, да и вобще псевдо элементы в динамике в ие8 к сожалению практически не работают.
что-то как-то кривовато…лучше бы разобрались до конца и выложили нормальное решение….
Нормальное решение — при избыточной стилизации (в данном случае наружняя тень у строки таблицы) использовать блоки, поскольку стилевые правила, применительно к таблицам, каждым браузером интерпретируются по-своему. Таблицы — для табличных данных.
Спасибо, нужно было строчно решение по похожей задаче, ваш вариант очень помог.
Версия для динамической высоты строки, но фиксированной ширины таблицы.
Псевдокласс генерируется в конце последней ячейки строки — связанно с багом отображения тени (если как у Вас)
http://jsfiddle.net/Hsw6N/1/
поправочка — работает только в chrome((
Можно убрать border-collapse: collapse; и поставить border-spacing: 0px 10px;
Ячейки строки и строку покрасить в один цвет. В принципе все, отступ даст пространство для hover. И все браузеры будут довольны