Rey Bango
I’ve had the good fortune of working with my friend Jonathan Sampson recently on figuring out how to help developers build plugin-free experiences. With IE10 Metro going plugin-free, it’s incredibly important to document steps to help developers provide their users with great experiences without the need for proprietary 3rd party add-ons.If you’ve built a plug-in-free browsing experience for the iPad, a few changes will make it ready for the new IE10 plug-in-free experience on Windows 8. As more browsers adopt the plug-in-free approach, now is a good time to start thinking about it. I’ll show you how to do this in a few steps below by writing code that works well in all modern browsers.
Today we’re going to work with a MSNBC plug-in-free experience for rich media. It breaks down to two things: styles and scripts.
To modify the files of MSNBC, I will be using a proxy application known as Fiddler. You can download this tool from http://fiddler2.com. This tool allows me to modify remote files as though they were on my local machine. If you have direct access to your own site, you can ignore Fiddler, and work directly with your files. Fiddler provides a great way for testing changes without the risk of breaking your live site.
Step 1: Declare Standards mode and valid markup for modern browsers
In order to use the HTML5 elements we’ll be utilizing below, you’ll first need to ensure that you are operating in standards mode. One way to ensure this is to include the HTML5 doctype at the top of your document:<!DOCTYPE html> |
Step 2: Update your CSS vendor prefixes
The CSS language is constantly in a state of change as new features are suggested, updated, and standardized. In order to allow developers to learn how to use these new features, browser vendors typically offer experimental implementations via prefixed properties.A key part of using vendor prefixes responsibly is to ensure that prefixes from each vendor are included in your site to allow for the broadest level of feature support. In many cases, especially when building an iPad-centric site, you may have focused solely on -webkit properties, omitting the prefixes which target other browsers such as -o, -ms, and -moz. The end result of this is that you greatly limit the target devices that can render your plugin-free site to as well as provide a degraded experience for users of other modern browsers, many of which could serve up equally engaging functionality.
For instance, we find the following on 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)) ); |
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% ); |
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 } |
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]; } |
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 } |
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 } |
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:
- Right-click this session and select “Decode Selected Sessions” from the context menu.
- Select the AutoResponder tab on the right.
- Click the “Enable automatic responses” checkbox in the AutoResponder tab.
- Drag the selected session from the left panel into the AutoResponder tab.
- If URI matches: EXACT:http://www.msnbc.msn.com/id/37156949/
- Then respond with: *200-SESSION_3
if(!(navigator.userAgent.toLowerCase().indexOf("ipad")>-1)){ // Flash Experience } |
if ( !document.createElement("video").canPlayType ) { // 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); } |
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); |
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); } |
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 */} elseif ( event.type == "touchmove" ) { /* Drag logic */} elseif ( event.type == "touchend" ) { /* Complete drag logic */} |
if ( event.type.match( /(down|start)$/i ) ) { /* Start drag logic */} elseif ( event.type.match( /move$/i ) ) { /* Drag logic */} elseif ( event.type.match( /(up|end)$/i ) ) { /* Complete drag logic */} |
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; |
var curX = event.pageX || event.targetTouches[0].pageX; |
Another important item to keep in mind is that this site initially responds to touchmove. When this event is raised while touching the playlist, the code attempts to reposition the playlist based upon your touch movement. There is no hovering when it comes to touch – you’re either touching, or you’re not.
Now that we have mouse events tied into this logic, we have introduced the possibility for hovering. So while touchmove is free to reposition our playlist when it is over the playlist, we don’t want to do the same for mousemove. In fact, we only want the mousemove event to reposition the playlist when the mouse button is pressed.
For further reading, and examples on how to target all browsers, see Handling Multi-touch and Mouse Input in All Browsers.
Testing both experiences
Recall our feature detection from earlier, how we first check to see if HTML5 video support is in the user’s browser. If it is, we give them HTML5. If it is not, we give them Flash. One easy way to test our work is to use a browser, or document mode, that doesn’t support HTML5 features. This is very easy to test with Internet Explorer:- Press F12 to reveal the Developer Tools
- Change your Document Mode to Internet Explorer 7 Standards
- Refresh the page
Get it Done!
Hopefully this post helps to define the types of changes that will allow your iOS site to work properly in IE10 Metro and other plugin-free environments. By including best practices such as feature detection and responsibly using vendor prefixes for great new features, you should be able to provide your users a great experience, regardless of which browser or device they’re using. To assist with testing in other plug-in-free environments, download Internet Explorer 10 (currently available only in the Windows 8 CP) and begin testing today!Update: In the rush to get this post up, I realized that I forgot to thank and give credit to Jonathan Sampson for helping investigate and write about the great techniques mentioned above. He was a huge help in generating many of these great techniques. Thanks JS!

No comments:
Post a Comment