IE9: Как не надо проверять версию браузера или “почему не работают фотки ВКонтакте”

Одна из всем известных проблем веб-разработки, или точнее веб-верстки, – это вопрос совместимости создаваемой или генерируемой верстки, включая разметку на HTML и стили на CSS, и используемых функций (API) на JavaScript в различных версиях различных браузеров. В общем случае, это касается далеко не только Internet Explorer и в частности IE6 и IE7, но и всех других браузеров, т.к. у всех есть баги и это просто данность с которой можно мириться или не мириться и, к примеру, активно постить отчеты об ошибках на Microsoft Connect.

Сегодня мы начнем наше погружение в вопросы совместимости с одного неправильного сценария и характерного примера проблем, к которым это может приводить.

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

Итак, мы имеем сайт и некоторое количество браузеров, которые нужно “детектировать”, так как в зависимости от браузера или его версии, поведение браузера на одном и том же коде может отличаться. Определять, кто именно к нам пришел, можно как на стороне сервера, так и на стороне клиента, как по каким-то очевидным признаками вроде User Agent String, так и по косвенным, вроде особенностей поведения или поддержки той или иной функциональности.

 // Conditional Comments
<!--[if IE]><![endif]-->

// Unique Objects
if(document.all) …
if(window.attachEvent) …
if(window.ActiveXObject) …

// Unique Behaviors (CSS Hacks, etc.)
* html {}

На стороне клиента довольно часто можно видеть код примерно следующего содержания (пример с vk.com):

 var _ua = navigator.userAgent.toLowerCase();
var browser = {
  version: (_ua.match( /.+(?:me|ox|on|rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1],
  opera: /opera/i.test(_ua),
  msie: (!this.opera && /msie/i.test(_ua)),
  msie6: (!this.opera && /msie 6/i.test(_ua)),
  msie7: (!this.opera && /msie 7/i.test(_ua)),
  msie8: (!this.opera && /msie 8/i.test(_ua)),
  mozilla: /firefox/i.test(_ua),
  chrome: /chrome/i.test(_ua),
  safari: (!(/chrome/i.test(_ua)) && /webkit|safari|khtml/i.test(_ua)),
  iphone: /iphone/i.test(_ua),
  ipod: /ipod/i.test(_ua),
  iphone4: /iphone.*OS 4/i.test(_ua),
  ipod4: /ipod.*OS 4/i.test(_ua),
  ipad: /ipad/i.test(_ua),
  safari_mobile: /iphone|ipod|ipad/i.test(_ua),
  mobile: /iphone|ipod|ipad|opera mini|opera mobi/i.test(_ua)
}

Из этого примера хорошо видна мина отложенного действия с Internet Explorer, которая обязательно сработает с выходом новой версии IE (и сработала с выходом IE9 Beta), не говоря уже о “хитростях” с Opera и разделением Сhrome и Safari, которые оба базируются на Webkit, но не совсем одинаково.

(Если код выше вам кажется слишком сложным, значит вы еще не видели версию от Apple .)

Почему сработает мина, описанная выше? Она не учитывает будущего. Поэтому далее в коде (особенно большом) вполне могут появляться (и появляются) участки, выглядящие примерно так:

 if (browser.msie && !browser.msie8){
   ...
} else {
   ...
}

Это, очевидно, приводит к тому, что для IE9 выполняется точно такая же ветка кода, как для IE6 и IE7, и это не является запланированным разработчиками сценарием. Бум-с! Мы получаем фотографии ВКонтакте, которые не листаются в IE9.

Вы можете детально посмотреть, как это работает с помощью отладчика в Dev Tools (F12). Также вы можете через Dev Tools поменять User Agent String, к примеру, на Firefox, и убедиться, что в этом случае все работает корректно.

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

Условие, приведенное выше, может показаться естественным (и даже экономным – вместо двух проверок сделали одну), если отталкиваться только от настоящего:

image

Но как только на горизонте появляются новые вводные, условие перестает работать “ожидаемым” способом и начинает делать именно то, что в нем прописано:

image

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

image

Если хочется отталкиваться от номера версии, устанавливая одну “разделительную черту”, то лучше явно разделять проверкой на “больше-меньше” номера версии, но никак не отрицанием одного конкретного варианта:

image

И просто для понимания: есть случаи, когда OS или браузер действительно нужно учитывать, причем не из-за багов или пробелов в поддержке того или иного стандарта. Например, в том же ВКонтакте пользователям Mac-браузеров в некоторых сценариях предлагается использовать Meta вместо Ctrl, которого у них нет.