Статьи

Метро от iOS до IE10: создание кросс-браузерного опыта без плагинов

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

Если вы создали браузер для iPad без использования плагинов, несколько изменений сделают его готовым к новому интерфейсу IE10 без плагинов в Windows 8. По мере того, как все больше браузеров применяют подход без плагинов, сейчас хорошее время, чтобы начать думать об этом. Ниже я покажу вам, как это сделать, написав код, который хорошо работает во всех современных браузерах.

Сегодня мы собираемся поработать с MSNBC без плагинов для мультимедиа. Он состоит из двух вещей: стили и сценарии.

Чтобы изменить файлы MSNBC, я буду использовать прокси-приложение, известное как Fiddler. Вы можете скачать этот инструмент с http://fiddler2.com . Этот инструмент позволяет мне изменять удаленные файлы, как если бы они были на моем локальном компьютере. Если у вас есть прямой доступ к вашему собственному сайту, вы можете игнорировать Fiddler и работать напрямую со своими файлами. Fiddler предоставляет отличный способ тестирования изменений без риска взлома вашего живого сайта.

Шаг 1: Объявите режим стандартов и допустимую разметку для современных браузеров

Чтобы использовать элементы HTML5, которые мы будем использовать ниже, сначала необходимо убедиться, что вы работаете в стандартном режиме. Один из способов убедиться в этом — включить тип документа HTML5 вверху документа:


<! DOCTYPE html>

Шаг 2: Обновите свои префиксы вендора CSS

Язык CSS постоянно находится в состоянии изменения, поскольку новые функции предлагаются, обновляются и стандартизируются. Чтобы позволить разработчикам узнать, как использовать эти новые функции, поставщики браузеров обычно предлагают экспериментальные реализации через префиксные свойства.

Ключевой частью ответственного использования префиксов поставщиков является обеспечение того, чтобы префиксы от каждого поставщика были включены в ваш сайт, чтобы обеспечить широчайший уровень поддержки функций. Во многих случаях, особенно при создании сайта, ориентированного на iPad, вы можете сосредоточиться исключительно на свойствах -webkit , не используя префиксы, предназначенные для других браузеров, таких как -o, -ms и -moz . Конечным результатом этого является то, что вы значительно ограничиваете целевые устройства, которые могут визуализировать ваш сайт без плагинов, а также предоставляют ухудшенный опыт для пользователей других современных браузеров, многие из которых могут обеспечивать одинаково привлекательную функциональность.

Например, мы находим следующее в MSNBC :

background: -webkit-gradient(
  linear,
  left top,
  left bottom,
  color-stop(1, rgba(192,192,192,.6)),
  color-stop(0.5, rgba(0,0,0,.6))
);

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

background: -webkit-linear-gradient(
  top, rgba( 0, 0, 0, 0.0 ) 0%, rgba( 0, 0, 0, 0.6 ) 50% );
background: -moz-linear-gradient(
  top, rgba( 0, 0, 0, 0.0 ) 0%, rgba( 0, 0, 0, 0.6 ) 50% );
background: -ms-linear-gradient(
  top, rgba( 0, 0, 0, 0.0 ) 0%, rgba( 0, 0, 0, 0.6 ) 50% );
background: -o-linear-gradient(
  top, rgba( 0, 0, 0, 0.0 ) 0%, rgba( 0, 0, 0, 0.6 ) 50% );
background: linear-gradient(
  top, rgba( 0, 0, 0, 0.0 ) 0%, rgba( 0, 0, 0, 0.6 ) 50% );

 

Более подробный, но преимущества расширенной поддержки функций браузера, безусловно, перевешивают дополнительную вводимую информацию. Кроме того, в предстоящем выпуске Visual Studio 2011 есть ряд отличных инструментов, которые могут разбить эту рабочую нагрузку, таких как SASS и Compass , без префиксов или даже фрагменты CSS .

Also, if you’re working predominantly in JavaScript and would like to save time determining which features are supported by your client’s browser, review the instructions in A Best Practice for Programming with Vendor Prefixes on the IEBlog.

Step 3: Get rid of browser sniffing methods

There are two methods used to determine what the user’s browser and device are capable of. One method, which unfortunately is somewhat popular, is browser sniffing. This method consists of examining the navigator object for certain patterns or values.

if ( navigator.userAgent.indexOf("iPad") > -1 ) {
  // Load HTML5 Experience
} else {
  // Load Flash Experience
}

The above code looks at the user agent string for the value “iPad”, and if found delivers a plug-in-free HTML5 experience. Otherwise, it’s assumed you are on a device that has Flash installed. This will result in a broken experience for non-iPad users who are browsing with plug-ins disabled, even though their browser is capable of handling HTML5 features.

Here is an attempt to find the version of Internet Explorer.

if ( tests.IE ) {
  j = /msie.(\d\.\d+)/i;
  k = navigator.userAgent.match(j)[1];
}

The user agent string is tested for a pattern that attempts to target the version number. This pattern looks for a single digit, followed by a period, followed by any number of additional digits. While this test will find values like “MSIE 8.0” and “MSIE 9.0”, it will not identify the latest version of Internet Explorer, which identifies itself as “MSIE 10.0”, since only one digit is expected before the period.

These are just a couple examples of why browser sniffing is not a best practice. The user agent string is not immutable – it is a read-write value that is easily changed by plugins, or even the user. Most modern browsers include the ability to easily change this value from their development tools, which some users take advantage of to get around poorly-developed websites.

If we disable plugins, or visit MSNBC from a device/browser that doesn’t have Flash, we would expect it to attempt a plug-in-free experience. Unfortunately, this is not the case. Rather than seeing an HTML5 experience, we’re instead asked to download Flash. This is because the site puts the user in one of two categories: an iPad user, or a Flash-enabled user.

Feature Detection

Rather than trying to guess what a browser is capable of by sniffing its user agent string (which will fail you eventually), it is much wiser to actually test features directly in the browser. If you wanted to test the browser’s ability to deliver video and audio via HTML5, you could actually attempt to create these elements via JavaScript, and see if the browser understands them. This practice is called feature detection.

if ( !!document.createElement(“video”).canPlayType  ) {
  // Load HTML5 Video
} else {
  // Load Flash Video
}

In the above example, we start by testing whether the canPlayType method exists on our newly-created video tag. We’re using double-negation to cast the response to a boolean. If the browser understands what a video element is, the canPlayType method will be present. If the video element is unknown to the browser, the canPlayType method will not exist. If this test passes, we load our HTML5 video. If the test does not pass, we attempt to load Flash. Deeper feature detection could take place here, since Flash may not be on the machine, or may be disabled.

Feature detection is the preferred method of determining what a browser is capable of, since there is no guesswork involved. If the browser passes properly-constructed tests, it absolutely supports the features you would like to use.

Many great tools exist to provide feature tests for you. Once such tool, which provides over 40 tests, is Modernizr. This tool creates a global object called “Modernizr” which contains the results of your tests. With Modernizr, testing for HTML5 video support is extremely easy:

if ( Modernizr.video ) {
  // Load HTML5 Video
}

MSNBC engages in browser sniffing to see if the device accessing the page is an iPad or not. Our first step is to remove the browser sniffing code, and replace it with feature detection code.

Before we can modify browser sniffing code, we first need to locate it. While in Internet Explorer, pressing F12 will pull up our Developer Tools. Within the tools, open the Script tab and do a search for “userAgent”. This search will seek out any instance of this property name in all of the site’s script files. We’re interested in the result from line 41 of http://www.msnbc.msn.com/id/37156949/.

Now that we know what we want to edit, we can open up Fiddler and load up our traffic. Once Fiddler is opened, perform a hard-refresh (Ctrl+F5 in IE) on the MSNBC page. This results in all of the page sessions being listed in Fiddler.

Looking carefully, you’ll notice our resource is the third from the top. Next I will setup an AutoResponder for this session file so that anytime it is requested, my own custom file is substituted in the place of the server response:

  1. Right-click this session and select “Decode Selected Sessions” from the context menu.
  2. Select the AutoResponder tab on the right.
  3. Click the “Enable automatic responses” checkbox in the AutoResponder tab.
  4. Drag the selected session from the left panel into the AutoResponder tab.

At this point, you should have an entry within your AutoResponder tab with the following rules:

  • If URI matches: EXACT:http://www.msnbc.msn.com/id/37156949/
  • Then respond with: *200-SESSION_3

Right-click the entry in the AutoResponder and select Edit Response. In the popup that follows, switch to the SyntaxView tab where we will find the source for this file. As expected, line 41 contains our browser sniffing code:

if(!(navigator.userAgent.toLowerCase().indexOf("ipad")>-1)){
  // Flash Experience
}

 Rather than test the contents of the userAgent, we’re going to instead look for support for the HTML5 video tag. Switch the above condition to the following:

if ( !document.createElement("video").canPlayType ) {
  // Flash Experience
}

This test checks to see if we cannot use the video element. If canPlayType comes back as undefined, it will be cast to true and the first code block will be entered, setting up the Flash experience.

Step 4: Update touch and pointer events

Safari supports both a touch event model and a mouse event model. Internet Explorer 10 groups touch, mouse, and stylus events into a single abstract item known as a pointer. In fact, Internet Explorer 10 is the first browser to work for all input types, across all devices. This abstraction cuts down drastically on the amount of effort involved to determine which event model you ought to bind to and how to detect user-interaction. This pointer is then handled through MSPointer events. If necessary, you can determine the type of pointer by accessing the pointerType property.

Due to the fact Internet Explorer doesn’t support Apple’s proprietary event model, which includes touch events like touchstart, touchmove, and touchend, MSNBC’s event listeners will need to be amended to listen for MSPointer events like MSPointerDown, MSPointerUP, and MSPointerMove.

Due to the difference in event model implementations, use a feature detection tool like Modernizr or code like this to target all major event models:

if (window.navigator.msPointerEnabled) {
  myCanvas.addEventListener("MSPointerMove", paint, false);
} else {
  myCanvas.addEventListener("mousemove", paint, false);
  myCanvas.addEventListener(“touchmove”, paint, false);
}

MSNBC only supports touch events, which we will need to change so that visitors who happen to be using a mouse can still interact with the page:

Our events are tied up in http://www.msnbc.msn.com/id/43662671/15:

document.addEventListener("touchstart", touchHandler, false);
document.addEventListener("touchmove", touchHandler, false);
document.addEventListener("touchend", touchHandler, false);

We’re going to update this to include the MSPointer events as well:
if (window.navigator.msPointerEnabled) {
  document.addEventListener("MSPointerDown", touchHandler, false);
  document.addEventListener("MSPointerMove", touchHandler, false);
  document.addEventListener("MSPointerUp", touchHandler, false);
} else {
  document.addEventListener("touchstart", touchHandler, false);
  document.addEventListener("touchmove", touchHandler, false);
  document.addEventListener("touchend", touchHandler, false);
  document.addEventListener("mousedown", touchHandler, false);
  document.addEventListener("mousemove", touchHandler, false);
  document.addEventListener("mouseup", touchHandler, false);
}

First we’re checking for the presence of pointers. Since the MSPointer covers the mouse, fingers, and pens, we don’t need anything else besides them. We fall back, if necessary, to provide both touch and mouse events.

Next we need to create cases for these event types in http://www.msnbc.com/id/44937131/. Currently, MSNBC starts with the following:

if ( event.type == "touchstart" ) {
  /* Start drag logic */
} else
if ( event.type == "touchmove" ) {
  /* Drag logic */
} else
if ( event.type == "touchend" ) {
  /* Complete drag logic */
}

We’ll modify this to listen for all of the registered event types:
if ( event.type.match( /(down|start)$/i ) ) {
  /* Start drag logic */
} else
if ( event.type.match( /move$/i ) ) {
  /* Drag logic */
} else
if ( event.type.match( /(up|end)$/i ) ) {
  /* Complete drag logic */
}

The above uses the match method and a series of regular expressions to determine which event was raised. If the event raised ends with a case-insensitive “down” or “start”, we begin our drag code. If the event ends with a case-insensitive “move”, we perform the actual drag logic itself. And lastly, if the event ends with a case-insensitive “up” or “end”, we end our dragging event. Note: other events may be caught here as well, like onresizeend and keyup. Be sure to consider this in your project.

The above is an implementation of Ted Johnson’s solution in Handling Multi-touch and Mouse Input in All Browsers.

The drag logic itself initially relies upon the event.targetTouches TouchList. This member does not exist in Internet Explorer. The drag logic attempts to gather the pageX and pageY properties from the first item in the TouchList, however in Internet Explorer these values are found directly on the event object.

var curX = event.targetTouches[0].pageX;

 Using the logical OR operator, I instruct curX to hold the value of event.pageX as long as event.pageX is present on the event object. If this property is not found, look within the targetTouches list:

var curX = event.pageX || event.targetTouches[0].pageX;

 

Если event.pageX не найден, мы возвращаемся к присвоению значения targetTouches [0] .pageX нашей переменной.

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

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

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

Тестирование обоих опытов

Вспомните наш способ обнаружения ранее, как мы сначала проверяем, есть ли поддержка HTML5-видео в браузере пользователя. Если это так, мы даем им HTML5. Если это не так, мы даем им Flash. Один из простых способов проверить нашу работу — использовать браузер или режим документов , который не поддерживает функции HTML5. Это очень легко проверить с помощью Internet Explorer:

  1. Нажмите F12, чтобы открыть Инструменты разработчика
  2. Измените режим документов на стандарты Internet Explorer 7
  3. Обновите страницу

Если наше условие обнаружения функции было написано правильно, вы должны смотреть презентацию на основе Flash. Переключение режима документов обратно на стандарты Internet Explorer 9 (или «Стандарты», если вы используете IE10) вернет вас к работе с HTML5.

Сделай это!

Надеемся, что этот пост поможет определить типы изменений, которые позволят вашему сайту iOS нормально работать в IE10 Metro и других средах без плагинов. Включая лучшие практики, такие как обнаружение функций и ответственное использование префиксов поставщиков для новых замечательных функций, вы сможете предоставить своим пользователям отличный опыт независимо от того, какой браузер или устройство они используют. Чтобы помочь с тестированием в других средах без подключаемых модулей, загрузите Internet Explorer 10 (в настоящее время доступен только в Windows 8 CP) и начните тестирование уже сегодня!

Обновление: в спешке, чтобы поднять этот пост, я понял, что забыл поблагодарить и отдать должное Джонатану Сэмпсону за помощь в исследовании и написании о замечательных техниках, упомянутых выше. Он очень помог в создании многих из этих замечательных техник. Спасибо JS!