Sunday, January 18, 2015

Microsoft 70-486: Enhance application based on browser feature detection

Exam Objectives


Detect browser features and capabilities; create a web application that runs across multiple browsers and mobile devices; enhance application behavior and style by using vendor-specific extensions, for example, CSS

Quick Overview of Training Materials



Feature Detection


Because web content can be consumed by such a wide variety of clients, it is impossible to take for granted what capabilities your users will have.  As browsers have advanced, they have steadily added functionality, but not all users have kept up with the times, and may have out of date browsers.  In other cases, browsers may have less functionality by design (they may be lightweight for low resource systems).  In any case, it is necessary to ascertain the capabilities of the client system when serving up the application to make the user experience as good as possible.

Using the "user-agent" header value to sniff out the browser is tricky and unreliable, and most sources will call it bad practice in most cases.  Both the stack overflow page and the Mozilla article point out that is may be appropriate when trying to work around a known bug in a particular browser.  However, if you are checking because you want to use a certain feature, then "feature checking" is more appropriate.  For the sake of completeness, I'll include the points Mozilla makes regarding doing "browser checking" as well as it can be done:

  • Need to distinguish between "browser" (Chrome, Firefox, Safari, IE) and render engine (Webkit, Blink, Gecko, Trident).  Targeting a render engine can be less exclusive (less well known browsers may be built on a more common render engine).
  • Look for the string "Mobi" to target mobile devices, rather than taking for granted that a given OS or render engine is mobile.
  • If you DO target the browser name, don't use overly simplistic regex.  User agent string for Firefox must exclude Seamonkey, since Seamonkey includes Firefox in the string and may give a false positive for Firefox.  The same is true regarding Chrome with Chromium and Safari with Chrome AND Chromium.
  • Internet Exlorer doesn't follow the same conventions (shocker).  Look for "MSIE xxx" where xxx is the version number.  The render engine is located in the  comment part of the user agent string.
However, the Jibbering article points out that the user string is not a reliable identifier of browsers anymore because lesser knows browsers tend to spoof (imitate) the more common browser to avoid being shut out of code they are otherwise capable of rendering. The most direct and reliable way of ascertaining if certain objects or functions are supported is to test for them directly.  This often follows the pattern if (<feature>) { use feature } else if ( <alternate feature> ) { use alternate feature } else { some safe fallback behavior }. Feature detection is not bulletproof, however.  A blog post by John Resig points out some of the limitations and unintended side effects that can come with feature detection.

Modernizr is a JavaScript library for feature detection.  It tests for support of HTML5, CSS3, and various APIs such as localstorage and websockets.  Modernizr is included in the MVC template in VS2013.  Modernizr tests can be used to determine if it is necessary to load a polyfill library for necessary functionality.  The detecting devices and their features article describes a method of passing the detected capabilities back to the server via the cookie (for browsers that support cookies and JavaScript anyway...)

Asp.net includes a built in mechanism for determining what a browser can do: HttpBrowserCapabilities.  The Request object has an instance of this class as a property called "Browsers" and it was quite interesting poking around in there in VS2013:


As an experiment I installed Midori and K-Meleon, which showed up as Chrome and Firefox respectively.  I also installed Lynx, which showed up as unknown.  I thought it would be an interesting contrast, so I grabbed the values out of the Capabilities property for both those browsers and compared them in a spreadsheet (below is the result)


Now, the MSDN passage for HttpBrowserCapabilities states that the Capabilities property is infrastructure and not intended for production code (begs the question as to why it isn't private but hey what do I know...).  The Lynx entry had 107 entries and Chrome 112, so there is probably some wonkiness going on behind the scenes that suggests we should probably take their word for it and use the other exposed testing properties instead for feature checking.

While the base version of this class provides some information, the MSDN article on detecting devices and their features points out that this class can be extended with third party libraries with much deeper databases of unique devices.  One use case for this data is to determine which of several possible optimized views to serve to the end user.  A less capable browser may receive static pages while a modern browser is served a single-page application style page.

Adaptive Rendering


In both the WROX and OReilly text, the first concept covered with regard to implementing adaptive rendering is the viewport, and how to manipulate it using the <meta> tag.  Simply put, the viewport is the rectangular viewing area rendered in the browser.  This doesn't really come into play on desktop browsers (I tried messing with the value to no effect), however mobile browsers use this information to determine how they render and zoom the content of a webpage.  Both texts contrast the following two meta values:

     
<meta name="viewport" content="width=device-width, initial-scale=1.0">
 
<meta name="viewport" content="width=320">
 

The first value is the default in the MVC5 template, and basically says the viewport should be fitted to the width of the device screen and zoomed with a factor of 1.  The second one states the viewport is 320 pixels wide, so the view will zoom in to that size.  This means devices with larger screens will be zoomed in too much, as can be seen in this comparison using the Google built in mobile emulators (a very cool feature I only just discovered):

How this page would look on an Apple iPhone5 in Landscape with the respective viewport values.
The idea behind using the device width as the viewport (rather than trying to use a fixed viewport) is that the design of the website should adapt to any device size (a concept also called responsive design).  This allows the website to display correctly not only on different devices (phone, tablet, desktop), but also on the same device as it changes orientation (turning a tablet or phone between portrait and landscape).  This is aided by the use of media queries.

I covered the basics of using media queries somewhat in the 70-480 post on adaptive UI.  To touch on feature detection again, it's important to ensure that you have a good set of default styles as not all browsers support media queries.  Media queries allow you to essential apply styles conditionally based on the specifics of the viewing devices capabilities.  For layout purposes, width is a common attribute to vary by, though it is also possible to query print to specify the styling for printing. Bootstrap does some of this by default in MVC5:
 
@media (min-width768px) {
  .dl-horizontal dt {
    floatleft;
    width160px;
    overflowhidden;
    clearleft;
    text-alignright;
    text-overflowellipsis;
    white-spacenowrap;
  }...and much more...


WROX and OReilly use almost exactly the same example to demonstrate how @media can be used to change the appearance of the site:

   
body {background-color:blue;}
@media only screen and (max-width800px) {
body {background-color:red;}
}
    

So if the site is narrower than 800px, it turns the background red.  Obviously this is a pretty pointless transform, but it illustrates the point (To their credit, the authors point out this silliness as well).  The example below shows both the silly example and the very useful Bootstrap transformation of the navigation menu on the narrower format (this is emulating a Nexus 7 in portrait and landscape):


Based on the striking similarities in this section, I have to wonder if one set of authors didn't have a copy of the other's book on hand when writing this section... maybe I'll give them the benefit of the doubt and assume they both worked, independantly, from a common source lol.

Vendor specific extensions


One of the rare opportunities the Exam Ref has shown some value, as it is the only book of the three to cover the vendor specific extensions, though it didn't go far beyond acknowledging that they exist and listing some.  The sitepoint article focuses on the CSS extensions, but there is other functionallity that uses the extensions as well.  One specific instance when I encounted these extensions was while I was puting together my websockets demo for the corresponding blog post, and the drawing demo called for using the .msToBlob() method on the canvas.  This example highlights both the benefit and pitfall of using browser extensions: they give you added functionality (the demo was super easy using the .msToBlob() method...) but they also can tightly couple your functionality with a specific vendor (the function only worked on IE and I needed it to work on Chrome).



It is pretty common to see these extensions on CSS properties that are not yet standardized, as each vendor will implement their own version.  I ran into this time and again while learning CSS, as it seemed half the examples on the w3schools website looked like this, yeesh:


The exam ref points out that browser extensions are supported long after they are necessary (meaning the standard behavior has been implemented) for the sake of backward compatibility.  Here you can see it with regard to linear gradients in Chrome.  Even though Chrome now supports the standard CSS linear-gradient property, it also still supportes the -webkit version:


The most common prefixes for web applications are webkit (Chrome and Apple), moz (Firefox), ms (IEx), and perhaps o (Opera).  I'm not going to list all the proprietary CSS attributes because there are about a billion and a half of them.  The consensus seems to be to avoid their use whenever possible.

See Objective 2.5 


There is a LOT of overlap between this objectives 2.4 and 2.5.  Study them together.  The divisions I made, content wise, between them were pretty arbitrary, as evidenced by the fact that they reference a lot of the same material.


No comments:

Post a Comment