Посты с тэгом «css»

  • Вышел Emmet v1.0

    Я рад сообщить, что после более полугода разработки в свет вышел Emmet (бывший Zen Coding) v1.0. Возможно, вы уже используете Emmet в течение нескольких месяцев, но только сейчас, после многочисленных исправлений ошибок и улучшений, я могу сказать, что он работает так, как надо.

    Что поменялось со времён Zen Coding?

    Во-первых, поменялось название. Emmet будет брэндом для новых инструментов и не все они будут связаны с написанием кода (coding).

    Во-вторых, у проекта появился полноценный сайт и обширная документация по всем возможностям. Теперь не надо искать в интернете обрывки информации о том, как пользоваться проектом, вся наиболее полная информация собрана в одном месте.

    В-третьих, улучшилась работа с CSS: значения свойств можно писать прямо в аббревиатуре. Также был учтён опыт и пожелания пользователей: благодаря модулю нечётного поиска вам необязательно запоминать громоздкие названия аббревиатур, достаточно написать всего несколько символов (например, ov:h == ov-h == o-h == oh == overflow: hidden);

    Вот список остальных значимых изменений:

    • Полностью переписан код проекта. Он стал более модульным и расширяемым.
    • Отказ от Python-версии. Мне было довольно сложно поддерживать две версии ядра. Вместо отдельной версии теперь используются мосты на Python, Objective-C и Java, это позволит править баги и добавлять новые возможности очень быстро и сразу на все платформы.
    • Улучшен модуль определения неявных имён тэгов. Ранее, если вы пытались развернуть аббревиатуру вроде .item, то в результате могли получить либо <div class="item">, либо <span class="item">, в зависимости от типа родительского тэга. Теперь модуль смотрит на название тэга и может вывести, например, <li>, <td>, <option>.
    • Поддержка расширений. Теперь, чтобы добавить новую аббревиатуру или настроить вывод результата, не надо лезть в код плагина, достаточно создать несколько простых JSON-файлов в специальной папке.
    • Генератор «Lorem Ipsum». Ранее, чтобы получить «рыбный» текст для сайта, надо было пользоваться сторонними ресурсами, а затем форматировать результат. Теперь получить такой текст можно прямо в редакторе, причём количество слов в тексте можно регулировать просто дописав число после аббревиатуры. Более того, генератор использует все возможности аббревиатур Emmet, позволяя дописывать нужные атрибуты к генерируемым элементам и регулировать количество создаваемых блоков.
    • Новый оператор ^. Несмотря на то, что в Emmet/Zen Coding довольно давно существует более мощный инструмент в виде группировки элементов, зачастую осознание того, что следующий элемент в аббревиатуре должен находится уровнем выше, приходит довольно поздно. Пользователям приходилось возвращаться обратно, добавлять скобки и дописывать нужный элемент. Теперь достаточно написать оператор ^, чтобы подняться на уровень выше, причём можно использовать несколько операторов подряд.

    Исходный код и плагины доступны в специальном репозитории. Если вы обнаружили ошибки или у вас есть пожелания по улучшению, буду рад узнать о них в разделе Issues.

    Метки: , ,
  • Веб-разработка в Eclipse: HTML и CSS

    Когда-то давно, работая ещё в Студии Лебедева, я рассказывал про использование Eclipse IDE в веб-разработке. Тогда моими основными инструментами были Aptana для HTML и CSS и Spket IDE для JS. С тех пор много воды утекло: Aptana всё дальше отходила от идей Eclipse IDE, всё больше превращаясь в самостоятельный продукт для Rails-разработки, а Spket IDE не обновлялся с октября 2009. В это же время проекты, с которыми я работаю, становились всё больше и сложнее и текущего инструментария уже не хватало. Руководствуясь принципом «если хочешь, чтобы что-то было сделано хорошо — сделай это сам» я принялся допиливать IDE до нужного мне состояния.

    Самая главная проблема всех редакторов для веб-разработки: они пишутся людьми, которые этой самой веб-разработкой не занимаются. Нет, они [программисты], конечно, могут на досуге собрать в своём редакторе сайтик-другой, но что делать тем, кто занимается веб-разработкой профессионально? Выход я вижу только один: взять хорошую платформу и доработать её до нужного состояния.

    Как я уже написал, Aptana превратилось в некого монстра, с избыточным для не-Rails технологам функционалом. Поэтому в качестве платформы я выбрал более простой и, главное, лучше интегрированный в IDE плагин — Eclipse Web Tools Project.

    Eclipse Web Tools Project

    Как можно понять из названия, Eclipse WTP — это инструменты для веб-разработки, а не законченный проект (как Aptana, например). Цель этого проекта: создать платформу, на основе которой можно создавать другие, узкоспециализированные инструменты. Этот проект включает в себя редакторы для CSS, HTML, XML, XSL, JavaScript (отдельный большой проект под названием Eclipse JSDT; о нём расскажу в следующих частях). Проект частично включён практически во все официальные сборки Eclipse, но для начинающих пользователей рекомендую ставить Eclipse for JavaScript Web Developers, в ней есть всё.

    Чтобы не пересказывать документацию, кратко приведу основные возможности:

    • подсветка кода;
    • сворачивание/разворачивание кусков кода (code folding);
    • outline и quick outline (на последний рекомендую обратить особое внимание, по умолчанию вызывается через Ctrl+O или ⌘O);
    • выделение границ тэгов (aka Balancing), смотрите в основном меню Edit > Expand Selection To;
    • content assist по тэгам, атрибутам и значениям некоторых атрибутов;
    • quick fix: переименование тэга (автоматически меняет открывающий и закрывающий тэги), обрамление тэгом;
    • форматирование всего/выделенного кода;
    • автоматическое закрытие тэгов.

    Итак, с платформой определились, теперь нужно её настроить для комфортной работы.

    Включаем поддержку HTML5 и CSS3

    Eclipse WTP в версии 3.3 (которая доступна для Eclipse Indigo) появилась поддержка тэгов HTML5 и некоторых свойств CSS3. Однако сразу они не доступны, нужно немного пошаманить с проектом:

    1. Открываем свойства проекта: Project > Properties.
    2. Переходим на вкладку Project Facets и жмём на ссылку Convert to faceted form…
    3. В появившемся окне выбираем фасет Static Web Module, жмём Apply.
    4. Закрываем окно с настройками проекта.

    Теперь в content assist будут доступны новые тэги и свойства. Если нет — снова открываем свойства проекта и идём на вкладку Web Content Settings, где настраиваем профили для HTML и CSS.

    Включаем проверку правописания

    Eclipse IDE с версии 3.4 (если мне не изменяет память) поддерживает проверку правописания, и по умолчанию доступен только английский словарь. Чтобы включить проверку правописания русского языка, делаем следующее:

    1. Скачиваем и распаковываем русский словарь.
    2. Открываем настройки Eclipse, идём в General > Editors > Text Editors > Spelling
    3. В User defined dictionary выбираем распакованный ru.dict.
    4. Ставим кодировку UTF-8

    Всё, теперь, если редактор, которым вы пользуетесь, написан по гайдам Eclipse Platform, вам будет доступна проверка правописания.

    Eclipse WTP Sugar

    Самую большую ломку при переходе с Aptana на Eclipse WTP у меня вызывало отсутствие content assist для путей к файлам (например, в <script src="...">): приходилось писать все пути к файлам вручную, что сильно утомляло. Да и в Aptana он был не идеальным: работал только внутри атрибута src (<script src="...">, <img src="..."> и т.д.), но не работал внутри href (<link href="...">, <a href="...">) и уж тем более не в CSS (background url(...), @import url(...)).

    Поэтому, потратив несколько месяцев на изучение Java и архитектуры Eclipse IDE, я написал свой content assist, который исправит этот недостаток, а заодно несколько других (о них чуть позже).

    Свой проект я назвал Eclipse WTP Sugar, репозиторий для установки: http://media.chikuyonok.ru/eclipse/webdev/updates/, исходный код доступен на GitHub.

    Плагин устанавливается очень просто, как и все остальные:

    1. Идём в Help > Install New Software…
    2. В качестве репозитория (поле Work with) вбиваем http://media.chikuyonok.ru/eclipse/webdev/updates/.
    3. В появившемся списке плагинов выбираем необходимые и жмём кнопку Next.
    4. Далее проходим стандартную процедуру установки (соглашаемся с лицензией, всё время жмём Next или Finish) и перезапускаем Eclipse.

    Теперь, при нажатии Ctrl+пробел внутри атрибутов src и href можно увидеть список файлов:

    ss01

    Список файлов также выдаётся и в CSS, внутри функции url():

    ss02

    Список файлов по возможности фильтруется. Например, если его вызвал внутри тэга <script>, то увидите список файлов с расширением js, если внутри <link rel="stylesheet"> — файлы с расширением css.

    Возвращаясь к разговору о том, что создатели программ для вёрстки сами толком сайты не верстают. Что если нужно использовать абсолютные пути для ссылок на файлы? По умолчанию считается, что абсолютный путь нужно резолвить относительно папки с проектом. Но почему никому не пришла в голову мысль, что веб-пространство проекта не всегда совпадает с корневой папкой проекта? В этом плагине проблема решена: в настройках проекта можно указать, относительно какой папки нужно резолвить абсолютные пути (Project > Properties > Document root):

    ss03

    Quick outline

    Следующее, что хотелось изменить, так это quick outline: специальное контекстное окошко, которое показывает в компактном виде текущую структуру документа:

    ss04

    Эту структуру можно фильтровать, чтобы найти нужны элемент (поиск осуществляется с начала строки, можно использовать символы * и ?). Но, как видно из скриншота, в структуре выводятся только названия тэгов, что делает её малопригодной. WTP Sugar исправляет этот недостаток, выводя гораздо больше полезной информации, по которой, в том числе, можно искать:

    ss05

    Eclipse XV Browser

    Не так давно я написал просмотрщик XML-файлов под названием XV Browser. В том посте я показал, что его можно использовать внутри Eclipse, однако не учёл один важный факт: работать это будет только если у вас Mac, а в качестве основного браузера используется Safari с установленным плагином XV :)

    Eclipse XV Browser исправляет этот недостаток. Он представляет из себя обёртку для встроенного браузера (это может быть Webkit либо Mozilla, в зависимости от ваших настроек и версии Eclipse). С помощью этого плагина можно прросматривать любой сайт в виде XML-структуры (Window > Show View > Other… > XV Browser), либо открывать просматривать XML-файлы проекта (правый клик по файлу, Open With… > XV Browser).

    Пожелания?

    Если есьт какие-то идеи по улучшению плагинов — пишите в Issues проекта. Пока в планах:

    • content assist по классам и идентификаторам, описанных в CSS;
    • быстрый переход к CSS-определению класса или идентификатора;
    • виджеты для редактирования CSS Gradients и CSS Box Shadow.

    В следующей статье рассмотрим, как улучшить работу с Eclipse JSDT (JavaScript).

    Метки: , , ,
  • Список блоков с разным вертикальным выравниванием

    Когда я работал над очередным улучшением сайта БигБаззи, возникла задача добавления нового режима просмотра предложений — по три штуки в ряд, более компактными блоками (было по два крупных блока в ряд):

    example

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

    «Классическое» решение проблемы — прописать минимальную высоту блоку с названием, чтобы угадать некую среднюю величину, выше которой вряд ли что-то появится. Но у этого способа есть два недостатка: во-первых, практика показывает, что обязательно появится название, которое не впишется в отведённое пространство и развалит весь ряд. А во-вторых: при слишком коротких названиях будут появляться неприятные дыры между заголовком и блоком с ценой.

    По сути, надо было придумать, как сделать так, чтобы блоки в одном ряду имели разное выравнивание по вертикали:

    scheme

    Такое расположение усложняется тем, что это не единственный режим просмотра: можно переключится на более крупный, двухколоночный режим. Причём желательно по-меньше трогать DOM-дерево — не делать специальных обёрток вокруг трёх или двух блоков, чтобы потом скриптом их перестраивать. Идеальный вариант — просто менять класс у контейнера и отдать всё изменение структуры на откуп CSS.

    Как ни странно, но решение задачи нашлось, причём довольно элегантное.

    Обычно такие последовательности блоков делаются самым очевидным образом: через float-элементы. Но этот способ нынче не в моде: все правильные ребята уже давно прочитали статью в блоге Мозиллы о том, как делать их через display: inline-block;.

    А что будет, если поставить рядом блоки разной высоты?

    <style type="text/css">
    	.wrap, .h, .f {
    		display: inline-block;
    	}
    
    	.h {
    		background: red;
    		height: 100px;
    		padding: 0 10px;
    	}
    
    	.f {
    		background: blue;
    		height: 20px;
    		padding: 0 10px;
    		color: #fff;
    	}
    </style>
    
    <span class="wrap">
    	<span class="h">header</span>
    	<span class="f">footer</span>
    	<span class="h">header</span>
    	<span class="f">footer</span>
    </span>
    

    Получим вот такой результат:

    ss1

    По умолчанию у всех блоков vertical-align: baseline, то есть вертикальное выравнивание по базовой линии (в данном случае, если проще — по последней строке текста). Если указать для .h{ vertical-align: top; } и для .f { vertical-align: bottom; }, то получим довольно интересное решение:

    ss2

    Блоки выровнены именно так, как нужно. Убеждаемся в правильности решения, изменив высоту одного из блоков:

    ss3

    Дальше всё просто: у шапки и подвала одинаковая и вполне определённая ширина, поэтому прописываем её каждому блоку, а подвал убираем из потока с помощью отрицательного margin’a:

    ss4

    Чтобы семантически объединить шапку и подвал, можно использовать любой элемент с display: inline. Итоговая версия доступна на тестовой странице.

    Самое интересное, что этот код работает даже в IE6. Помним, что inline-block в этом браузере эмулируется с помощью display: inline и волшебного hasLayout, например, так: display: inline; zoom: 1;.

    В этом решении важно помнить следующее:

    1. Шапка и подвал в примере пересекаются, поэтому шапке нужно снизу давать отступ и подтягивать подвал любым доступным способом.
    2. Между элементами конструкции не должно быть переводов строк и пробелов, иначе блоки визуально будут разделены.

    В данном случае мне очень помогло то, что у подвала вполне предсказуемая высота, поэтому у шапки был добавлен снизу большой фиксированный отступ, чтобы блоки не перекрывались. Это решение полностью решило проблему и отображения, и переключения режимов. Можно пойти дальше и подключить CSS3 Media Queries, чтобы обладатели новых браузеров и больших мониторов могли видеть более трёх предложений в ряду, но это уже другая история. Думаю, читатели смогут найти более творческие и интересные применения этого небольшого трюка :)

  • История одной оптимизации

    Летом мы запустили новый купонный проект BigBuzzy. Таких проектов к тому времени было довольно много и чтобы выделиться из толпы мы решили немного поменять бизнес-модель: вместо одного предложения в день выдавать четыре. Но, как это обычно бывает, аппетит приходит во время еды, поэтому уже спустя несколько месяцев на главной странице красовалось не 4, а 30 предложений.

    И мы сразу же начали получать жалобы о жутких тормозах на главной странице. На поиск и устранение проблем у меня ушло два дня. О том, как находились узкие места и будет сегодняшний рассказ. А заодно научимся пользоваться инструментами вроде Web Inspector’s Timeline (если вы их ещё не освоили).

    Поиск проблемы

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

    01-timers

    Самые большие проблемы наблюдались в Firefox: загрузка процессора на главной странице доходила до 70%. Поэтому я начал рассматривать скрипт таймера под микроскопом, а именно в Web Inspector, который по умолчанию входит в состав браузеров Safari и Chrome. Вообще, многие ребята довольно снисходительно относятся к этому инструменту, продолжая по привычке работать в Firebug’е, а зря. Лично для меня Web Inspector стал основным инструментом для отладки: выглядит он приятнее и содержит ряд полезных нововведений.

    Исследуем узкие места

    Так как сам скрипт таймера довольно простой, то не было смысла заниматься его профилированием — проблема явно где-то в reflow и repaint. Поэтому скрипт нужно исследовать через Timeline:

    02-timeline

    Полагаю, что многие читатели ещё ни разу не сталкивались с этим инструментом, поэтому принцип его работы и поиска проблем опишу в небольшом уроке. Стоит отметить, что Web Inspector в Chrome немного круче, чем в Safari, поэтому рекомендую пользоваться первым браузером.

    Timeline показывает нам практически все процессы, которые происходят в браузере: запуск скрипта, отработка события, перерисовка экрана, установка таймера, отправка аякс-запроса и т.д. Так как данных довольно много и в них легко запутаться, я рекомендую изолировать исследуемый скрипт — выделить его в отдельную страницу. Для практики можно начать с простого шаблона, на котором мы будем экспериментировать.

    Открываем шаблон в браузере и запускаем Web Inspector, вкладка Timeline. На странице есть красный квадратик и кнопка «Test». Чтобы начать исследование, нужно нажать на кнопку записи вкладки Timeline 03-rec, а потом нажать на кнопку «Test» в основном окне браузера. Наш квадратик посинел и стал больше по высоте, а в Timeline записались следующие события:

    04-test1

    Первые три записи относятся непосредственно к кнопке, которую нажали: применили псевдо-класс :active (Recalculate style), отобразили изменения на экране (Paint), вернули кнопку в исходное состояние, убрав :active (Recalculate style). После того, как пользователь отпустил кнопку мышки сработало событие click, и именно оно и всё, что ниже, нас будет интересовать.

    Во время клика сработал следующий скрипт:

    function test() {
    	var el = document.getElementById('test');
    	el.style.backgroundColor = 'blue';
    	el.style.height = '100px';
    }
    

    Ничего особенного: просто получили ссылку на элемент и поменяли у него цвет фона и высоту. Этот процесс был отображён на временной шкале: пересчитали стили (Recalculate style), пересчитали геометрию объектов (Layout) и отобразили изменения (Paint).

    Как видите, несмотря на то, что мы поменяли два СSS-свойства, пересчёт стилей произошёл всего один раз. Поменяем скрипт:

    function test() {
    	el.style.backgroundColor = 'blue';
    	var height = el.offsetHeight;
    	el.style.width = '100px';
    }
    

    Между присваиванием новых стилей мы решили получить высоту объекта. Но временная шкала сильно преобразилась:

    05-test2

    Теперь у нас уже два события Recalculate style, а у самого события click появилась группировка (треугольник слева от жёлтой полоски), которая указывает, какие именно события произошли во время клика.

    Этот небольшой пример указывает на две очень важные особенности браузеров — это откладывание перерисовки на момент выхода из функции (первый пример) и существование определённых свойств у элемента, которые принудительно вызывают пересчёт стилей (далее restyle; второй пример). О существовании особых свойств, вызывающих restyle, думаю, многие уже знали: это свойства вроде offsetLeft/Right/Width/Height, clientLeft/Right/Width/Height и так далее. Во втором примере, после установки свойства backgroundColor браузер пометил дерево элементов как требующего пересчёта стилей. А обращение к offsetHeight принудительно вызвало этот пересчёт. Затем мы установили свойство width, которое отложило пересчёт стилей, геометрии и отображения на момент выхода из потока JS-функций.

    Отсюда первое правило: нужно стараться не смешивать получение и запись CSS-свойств. Лучше, например, сначала получить нужные свойства элемента, а затем присвоить новые.

    Для любителей jQuery более красноречивым будет вот такой пример:

    function test() {
    	var e = $('#test');
    	var width = e.css('width');
    	if (width == '50px')
    		e.css('width', '100px');
    
    	var height = e.css('height');
    	if (height == '50px')
    		e.css('height', '100px');
    }
    

    Вот его шкала:

    06-test3

    Как видите, помимо лишнего Recalculate style появился Layout (reflow), что сделало выполнение скрипта более медленным. Если немного оптимизировать, переместив получение высоты выше в коде:

    function test() {
    	var e = $('#test');
    	var width = e.css('width'),
    		height = e.css('height');
    
    	if (width == '50px')
    		e.css('width', '100px');
    
    	if (height == '50px')
    		e.css('height', '100px');
    }
    

    …получим совершенно иную картину:

    07-test4

    Лишний Layout (помимо Recalculate style) объясняется тем, что jQuery каждый раз при получении CSS-свойств вызывал window.getComputedStyle(), который принудительно запускает reflow. Справедливости ради стоит отметить, что в функции jQuery.css() есть оптимизация, которая сначала проверяет наличие запрашиваемого свойства в element.style и если его там нет, вызывает window.getComputedStyle(). Но в любом случае, лучше всегда разделять чтение и изменение свойств.

    Таймеры

    Напомню, что я занимался оптимизацией таймеров, которых было несколько. И каждый таймер работает через свой setTimeout(). Посмотрим, что это означает на практике:

    function test() {
    	var el = document.getElementById('test');
    	setTimeout(function(){
    		el.style.backgroundColor = 'blue';
    	}, 10);
    	setTimeout(function(){
    		el.style.width = '100px';
    	}, 10);
    }
    

    08-test5

    У обоих таймеров одинаковый период ожидания и момент исполнения. На шкале видно, что после каждого таймера был запущен пересчёт стилей. Но в реальности момент исполнения будет далеко не всегда одинаковым. Поэтому поменяем задержку у последнего таймера — поставим 11 мс вместо 10 мс:

    function test() {
    	var el = document.getElementById('test');
    	setTimeout(function(){
    		el.style.backgroundColor = 'blue';
    	}, 10);
    	setTimeout(function(){
    		el.style.width = '100px';
    	}, 11);
    }
    

    И мы видим, что на шкале появился дополнительный repaint:

    09-test6

    В итоге получалось, что из-за нескольких запущенных setTimeout() срабатывали ненужные перерисовки, которые пользователь всё равно не увидит, но процессор это нагружало прилично. Я переписал код таймеров таким образом, чтобы всё работало через один глобальный таймаут.

    Итак, суммируя всё вышесказанное, для оптимизации я

    • разделил чтение и запись CSS-свойств;
    • дополнительно сделал кэширование текущих значений анимации, чтобы меньше обращаться к элементам;
    • заменил несколько таймаутов на один.

    В итоге в Firefox нагрузка на процессор снизилась… всего на 10%. Вообще, это было крайне странно: даже при наличии всего одного анимированного таймера на странице Firefox грузил процессор на 60%, при том что Webkit грузил всего на 5%. Нужно копать дальше.

    Влияние вёрстки на производительность

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

    Так как все restyle и reflow процессы я оптимизировал, проблема явно была где-то в repaint. Вспомнил, что в Firefox 3.5 появилось событие mozAfterRepaint, которое позволяет увидеть области, которые были перерисованы во время repaint. Для удобства было поставлено расширение Firebug Paint Events, которое позволяет отслеживать перерисовки экрана.

    Чтобы описать всю бурю эмоций, которые я испытал после просмотра логов, предлагаю читателю посмотреть на скриншот, где указана область перерисовки во время работы всего одного таймера на странице:

    10-bbz

    Я специально оставил только 8 из 30 предложений, чтобы картинка не распирала страницу, но смысл, думаю, ясен: во время анимации даже одного таймера перерисовывалось примерно 90% страницы, 15 раз в секунду. И это при условии, что у цифр таймера указан position:absolute, а у их контейнера overflow:hidden. То есть сама анимация по определению никак не могла повлиять на области вне контейнера (на скриншоте обозначен синим прямоугольником), но перерисовывалась почти вся страница.

    Около часа мне понадобилось на то, чтобы найти причину такого странного поведения. Ей оказалось… свойство float:left у одного из контейнеров. Как только я заменял его на float:none нагрузка на процессор падала ниже 10% (с float:left была около 60%).

    Проблема проявляется стабильно, причём не только в Firefox, но и в Opera и IE8. Я сделал простую демку, где можно в живую увидеть эту проблему. В ней всего несколько блоков, однако у них указан box-shadow — очень тяжелое в плане нагрузки на процессор CSS-свойство. В правом верхнем углу есть кнопка, которая всего лишь переключает float у контейнера. Понаблюдайте за нагрузкой на процессор при разных состояниях кнопки, а также за областью перерисовки.

    В общем виде проблему можно описать так:

    Repaint срабатывает на контейнере самого дальнего родителя, у которого указан float:left|right.

    Схематично это выглядит так:

    11-tree

    Причём проблема не только во float. Я перепробовал различные варианты горизонатльной группировки блоков: display:inline-block, display:table-cell, таблицы и даже новомодные flex box — во всех случаях проблема оставалось. Помогало только абсолютное позиционирование боковых блоков.

    В общем виде я проблему решил: поставил боковую панель в коде перед основным контейнером и только ей указал float. Основной контейнер был без float и repaint происходил именно там, где нужно. Однако на живом сайте решить проблему не удалось, так как на большинстве страниц стояли clearfix-элементы, из-за которых макет разваливался. Поэтому пришлось пока отключить анимацию с таймеров :(

    ***

    Честно говоря, после таких браузерных крендебобелей на всякие пузомерки типа Peackeeper, которыми так хвастаются разработчики с каждым новым релизом своего браузера, без слёз смотреть не получается. Поэтому мой вам совет: заранее узнавайте о всех интерактивных элементах на странице, не увлекайтесь новым CSS3, продумывайте рост сайта заранее и пользуйтесь правильными инструментами для отладки производительности — тогда будет вам счастье и высокая производительность.

    Если хотите узнать больше о профилировании производительности, очень рекомендую статью Стояна Стефанова на эту тему.

  • Идея для сниффинга браузеров

    Возникла одна идея, как можно скрестить клиентское и серверное «вынюхивание» браузера.

    Как это делается сейчас? На сервере мы смотрим на заголовок User Agent и определяем, что это за браузер. Но этот заголовок можно легко подменить (причём необязательно это делать в самом браузере, это может сделать прокси-сервер), поэтому такая проверка совсем ненадёжна. На клиентской стороне у нас гораздо больше возможностей: мы можем не только проверить User Agent браузера, но и его возможности (например, поддержку определённых CSS и JS свойств), что даст нам более точные характеристики браузера.

    Собственно, сама идея как раз заключается в том, чтобы скрестить эти два способа: определить с помощью CSS и JS характеристики браузера и в нужный момент сообщить их на сервер. Основа алгоритма — Chunked Transfer Encoding, позволяющий передать ответ (например, HTML-документ) не целиком, а кусками. Итак, сам алгоритм:

    1. Когда пользователь запрашивает HTML-страницу, отдаём ему первый фрагмент (chunk), в котором содержится sniffing-код. После этого соединение удерживается на некоторое время, чтобы отдать второй фрагмент документа.
    2. Браузер, получив первый фрагмент, тут же его парсит и пытается отобразить.
    3. В этом первом фрагменте у нас sniffing-код, результатом работы которого является CSS-свойство background-image, применяемое к элементу <html>.
    4. Путь к этой фоновой картинке является уникальным для каждого браузера и так уж получилось, что как только мы применили это свойство элементу, браузер открывает второе соединение и тут же пытается загрузить эту картинку.
    5. Сервер, получив запрос к нашей «картинке» понимает, каким браузером мы зашли и отдаёт вторую часть ответа в первом соединении.
    6. Если обращения к «картинке» не было (например, зашли поисковым роботом), то через какой-нибудь небольшой промежуток времени отдаём стандартный ответ.
    7. После первого «вынюхивания» можно поставить куку, чтобы потом сразу отдавать нужный ответ.

    В итоге получаем следующие преимущества:

    • Нет никаких редиректов: ответ о типе браузера получаем в одном соединении (полезно для SEO).
    • Очень сложно обмануть такой сниффер: можно до посинения менять строку User Agent, но результат останется прежним. Гугл сможет эффективней банить Оперу.
    • Более сложные проверки: можно не просто определять название браузера, но и его возможности, благодаря CSS Media Queries.

    Для желающих поэкспериментировать я написал демонстрацию этого подхода на node.js. К сожалению, у меня на VPS не хватает памяти, чтобы собрать node.js, поэтому предлагаю читателю самому его установить и проверить у себя. Если кто выложит это в онлайн — буду премного благодарен. Выложили: http://tbms.ru:8126/ (спасибо Николаю Митину).

    Полезные уроки

    Пока писал эту демонстрацию, вынес для себя много ценной информации:

    1. Первый чанк должен быть достаточно большим, чтобы все браузеры смогли его применить. Самый большой лимит — у движка Webkit: около 2 КБ. Поэтому приходится «добивать» первый фрагмент пробелами, чтобы всё работало как надо.
    2. Webkit очень плохо ведёт себя при переопределении CSS-свойств. Например:
      html { background-image:url(image1.png); }
      html { background-image:url(image2.png); }
      		

      В этом случае Webkit (и десктопный, и мобильный) загрузит обе картинки, хотя понятно, что нужна только последняя. Это ещё одно подтверждение, что мобильные версии сайта лучше делать отдельным проектом, а не вставлять через CSS Media Queries. Иначе попытки сократить траффик заменой больших фоновых картинок на маленькие приведёт к обратному эффекту. Хотя тут нужно больше исследований, возможно, это издержки работы с локальным сервером.

    3. Firefox не отработает первый чанк до тех пор, пока не получит элемент <body>. Это довольно серьёзный недостаток в том случае, если вы захотите выдавать уникальные стили и скрипты для браузера в секции <head>. С другой стороны, если послать <script>alert(1)</script>, то чанк отработает нормально, даже без <body>. Что наталкивает на мысль о существовании неких триггеров, которые заставят браузер сделать то, что нам нужно, осталось только найти наиболее безопасный.

    Повторюсь, что это только идея, а не призыв к действию. Буду рад, если кто-то на её основе сделает что-то крутое и интересное.

← cтарое