...

пятница, 15 ноября 2013 г.

[Из песочницы] Ширина столбцов таблицы или когда врут браузеры

Предыстория



История начинается с одного древнего проекта с web-интерфейсом написанным ещё под IE5-6. Разумеется этот мамонт под новыми версиями IE работает только в quirks mode, под остальными браузерами даже отрисовывается с трудом а про работоспособность мечтать и не приходится.

Одним светлым днём с небес прилетел глас начать постепенно переписывать это всё на современные браузеры и работа закипела.

99% системы представляли из себя реестры в виде таблиц и форм отдельной карточки из этого реестра. Заголовок таблицы должен быть фиксирован. В старой версии это делалось какими-то специфичными костылями c position которые не работали уже в IE7. jQuery уже был подключён, плагин для фиксированного заголовка таблицы гуглится легко. Не поддерживает заголовки с несколькими строками и различной комбинацией col и rowspan'ов? Ну ладно, можно и самому поработать немного, всё равно лезть в код плагина и адаптировать его под специфичную обёртку таблиц.

Казалось бы всё хорошо, но время от времени стали возникать артефакты в виде уползания столбцов на 1 пиксел, местами сдвиг пропадал или накапливался до 3-4 пикселов. Причём в Chrome данный глюк не наблюдался.

image



Тщетные попытки исправить ситуацию



Артефакт произвольно то проявлялся, то отсутствовал на различных таблицах и зависил также от ширина окна браузера. Ни у одного столбца таблицы не задана явно ширина. Поэтому первое что пришло в голову — это различия в ширине исходной таблицы и новой таблицы с фиксированным заголовком. Поигравшись с шириной так ничего не смог подогнать, если в одном месте таблица отображалась корректно, то там где она отображалась нормально начинала ломаться. Значит дело не в ширине.

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


Дальше я решил проверить ширину столбцов после отрисовки и форматирования таблицы-заголовка. Вдруг где-то была ошибка в разметке и ширина таблицы могла меняться после отрисовки нового элемента, но значения ширины оказались идентичны до и после. Никаких ухищрения со стилями вроде border-box и display нужного результата также дали — таблицы всё также различались.


Скандалы, интриги и расследования



Совсем уже отчаявшись взял в руки калькулятор, открыл firebug и начал складывать ширину каждого из столбцов в попытке найти где именно начинает уезжать таблица. И… firebug сказал что ширина у них всех одинаковая! Как же так? Они даже визуально разные. Может быть проблема в firebug'е в закладке с разметкой объекта? Смотрю в DOM'е — там тоже самое значение в clientWidth, встроенный инспектор в FF выдаёт аналогичные значения.

Глаз может и можно обмануть, а вот paint вряд ли. Копирую скрин окна браузера в редактор и начинаю считать ширину столбцов. Дохожу до первого кривого столбца и paint выдаёт ширину на 1 пиксел меньше того что говорит браузер! Возвращаюсь назад на страничку. Все инструменты браузера и jQuery мамой клянутся что ширина столбца 473 пиксела, но paint твёрдо настаивает что на самом деле 472. Сразу вспомнился монолог Задорнова про рулетку склеенную по середине. Неужели тёщи добрались и до разработчиков? К слову в IE ситуация точно повторяла то что происходит в IE. Заговор?

image

Следующий час я провожу в гугле в попытках найти существующие баги подсчёта ширины столбцов или какие-то особенности clientWidth или функции .width(). И безрезультатно. Всё что удаётся найти это либо про дробные значения в css либо явные косяки авторов разметки.

Напился (с)

Должен же быть способ узнать правильную ширину столбца, как-то таблица же рисуется браузером и ничто никуда не заползает. Ничего не остаётся кроме перебора возможных различий в свойствах ячеек. Вывожу свойства столбца из DOM исходной таблицы и сгенерированной плагином. Сравниваю. Вроде всё совпадает… кроме offsetLeft. Как же так? Вставляем костыль с расчётом ширины по смещению, для последней ячейки будем использовать обычную ширину:



this.nextSibling.offsetLeft - this.offsetLeft = 472



И вот теперь таблицы стали одинаковыми во всех имеющихся у меня браузерах.
Что же происходит



Разметка таблицы не содержит ширины столбцов и браузер автоматически подстраивает ширину столбцов по своему желанию опираясь на содержимое таблицы. Где-то рассчитанное значение ширины получается дробным, и тут-то по всей видимости начинаются проблемы реализации.

Так как дробную ширину нарисовать нельзя — значение округляется, и это округлённое значение подставляется в DOM. При определённых соотношения ошибка округления может накопиться и общая ширина таблицы вырастет или уменьшится. Если верить статье habrahabr.ru/post/31392/ то FF старается вписать содержимое ровно по размеру родителя и уменьшает значения некоторых столбцов на 1 пиксел чтобы они не превысили ширину таблицы, offsetLeft при этом изменяется у смещённых столбцов, а вот clientWidth нет! Поэтому при попытке создать копию заголовка таблицы сценарию возвращается не уменьшенная (или увеличенная) на 1 пиксел ширина и столбцы расползаются.

Другого варианта я не вижу.

Аналогичная ситуация и в IE, только там инспектор показывает дробное значение ширины столбца, в clientWidth округлённое значение, а отрисовано всё равно на 1 пиксел меньше. Тот же диагноз, только инспектор не врёт.

Дабы исключить ситуацию что какая-то ошибка в вёрстке страницы попробуем воспроизвести баг в тепличных условиях:


Скрытый текст


<!DOCTYPE html>
<html>
<head>
<script src="calc/jquery.js"></script>
<script>
$(document).ready(function(){
$(window).resize(function() {
$('#tr > *').each(function() {
var width1 = this.clientWidth;
var width2;

if ($(this).next('td').length) {
width2 = $(this).next('td')[0].offsetLeft - this.offsetLeft;
}
console.log(width1, width2);
});
});
});
</script>
</head>
<body>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr id="tr">
<td>1234556</td>
<td>123455644</td>
<td>12345</td>
<td>12345565645</td>
<td>123455643</td>
<td>12345563453433</td>
<td>12345565645</td>
<td>123455643</td>
<td>12345563453433</td>
</tr>
</table>
</body>
</html>





И действительно, у меня в FF при изменении ширины окна width1 и width2 иногда различаются на 1 пиксел. Причём различие может быть как в большую так и в меньшую сторону что исключает применение разных способов округления при заполнении DOM и при отрисовке страницы, а действительно проблема где-то на этапе “уплотнения” столбцов таблицы.


Безусловно расположение элементов с дробной шириной или высотой задача не тривиальная и может быть решена по разному, но несоответствие ширины указанной в DOM с реальной на странице я считаю не простительным.


Может я не прав в данной ситуации и багрепорт создавать не надо? Такое поведение вполне ожидаемо и в моём случае нужно использовать такой костыль для корректного выравнивания ширины столбцов? Хотелось бы услышать ваше мнение.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers.


Комментариев нет:

Отправить комментарий