A Best Practice for Programming with Vendor Prefixes

Vendor prefixes enable Web developers to experiment with new standards before they reach the Candidate Recommendation stage. I previously wrote how these prefixes are also a mechanism browser vendors use for handling timing conflicts between implementations and specifications. In building demos of new features for our IE Test Drive site and in various presentations, many of us on the IE team deal extensively with vendor prefixes.

This blog post describes a pattern we’ve used in some recent demos that’s making things significantly easier for us and has become a best practice. We’d like to share it with you and hear your thoughts on this approach or any others you consider a best practice.

Error-Prone Code

When using script to access CSS properties with vendor prefixes, it’s easy to end up with code that looks like this:

var elm = document.getElementById("myElement");

elm.style.msTransitionProperty = "all";

elm.style.msTransitionDuration = "3s";

elm.style.msTransitionDelay = "0s";

elm.style.webkitTransitionProperty = "all";

elm.style.webkitTransitionDuration = "3s";

elm.style.webkitTransitionDelay = "0s";

elm.style.MozTransitionProperty = "all";

elm.style.MozTransitionDuration = "3s";

elm.style.MozTransitionDelay = "0s";

elm.style.OTransitionProperty = "all";

elm.style.OTransitionDuration = "3s";

elm.style.OTransitionDelay = "0s";

While functional, it’s bloated, ugly, and error-prone.

Consolidating Vendor-Prefixed Properties to a Single Name

A better pattern is to define a method that loops through a list of property names and returns the first supported property or null if the browser doesn’t support any of them.

function FirstSupportedPropertyName(prefixedPropertyNames) {

var tempDiv = document.createElement("div");

for (var i = 0; i < prefixedPropertyNames.length; ++i) {

if (typeof tempDiv.style[prefixedPropertyNames[i]] != 'undefined')

return prefixedPropertyNames[i];

}

return null;

}

We then initialize a variable for each vendor-prefixed property we use, passing it an array of possible properties in the order we prefer to use them.

var transformName = FirstSupportedPropertyName(["transform", "msTransform", "MozTransform", "WebkitTransform", "OTransform"]);

var backfaceVisibilityName = FirstSupportedPropertyName(["backfaceVisibility", "msBackfaceVisibility", "MozBackfaceVisibility", "WebkitBackfaceVisibility", "OBackfaceVisibility"]);

var transitionName = FirstSupportedPropertyName(["transition", "msTransition", "MozTransition", "WebkitTransition", "OTransition"]);

var animationName = FirstSupportedPropertyName(["animation", "msAnimation", "MozAnimation", "WebkitAnimation", "OAnimation"]);

var gridName = FirstSupportedPropertyName(["gridRow", "msGridRow", "MozGridRow", "WebkitGridRow", "OGridRow"]);

var regionsName = FirstSupportedPropertyName(["flowFrom", "msFlowFrom", "MozFlowFrom", "WebkitFlowFrom", "OFlowFrom"]);

var hyphensName = FirstSupportedPropertyName(["hyphens", "msHyphens", "MozHyphens", "WebkitHyphens", "OHyphens"]);

var columnName = FirstSupportedPropertyName(["columnCount", "msColumnCount", "MozColumnCount", "WebkitColumnCount", "OColumnCount"]);

Then code throughout your site that uses these properties becomes something like this:

var elm = document.getElementById("myElement");

if (transitionName) {

elm.style[transitionName + "Property"] = "all";

elm.style[transitionName + "Duration"] = "3s";

elm.style[transitionName + "Delay"] = "0s";

}

else {

// fallback for browsers without CSS3 transitions

}

Note the simple feature detection enabled by returning null in FirstSupportedPropertyName.

That pattern also works when CSS properties have vendor prefixes. You can use a slightly different pattern for cases where a CSS value (for example, linear-gradient) has vendor prefixes:

function FirstSupportedFunctionName(property, prefixedFunctionNames, argString) {

var tempDiv = document.createElement("div");

for (var i = 0; i < prefixedFunctionNames.length; ++i) {

tempDiv.style[property] = prefixedFunctionNames[i] + argString;

if (tempDiv.style[property] != "")

return prefixedFunctionNames[i];

}

return null;

}

 

var linearGradientName = FirstSupportedFunctionName("backgroundImage", ["-ms-linear-gradient", "-moz-linear-gradient", "-webkit-linear-gradient", "-o-linear-gradient"], "(top, black, white)");

var radialGradientName = FirstSupportedFunctionName("backgroundImage", ["-ms-radial-gradient", "-moz-radial-gradient", "-webkit-radial-gradient", "-o-radial-gradient"], "(50% 50%, circle cover, black, white)");

Testing Sites that Use Vendor-Prefixed Properties

A common question is what property names to use if some browsers don’t yet support the property or if no browser supports the standards-based property without a prefix. There are a couple approaches, each with merit:

  1. Always include all expected names, even if they don’t yet work in shipping browsers. The benefit of this path is that as browsers add support with their vendor prefix or add support for the non-prefixed property, your site will “just work” without changes. The risk is that the site will automatically pick up behavior you’ve never tested. A vendor prefix indicates the behavior isn’t finalized and all prefixed properties and the non-prefixed property may not behave the same, so as browsers add support your site might “just not work.”
  2. Only include property names you can test. The benefit is that your site will behave the same as when you first wrote it even as browsers add support for new properties. The risk is that you have unnecessarily degraded functionality. For sample or demo sites, people can interpret this as a browser not having a feature at all.

You need to determine the right path for your site. In most of our demos we want to show off new Web platform functionality in any browser that supports it. And since small errors in these demos don’t create big problems for users, we usually choose option #1. On the other hand, if you have a production site where a change in behavior will cause a problem for your business, you may opt for the more risk-averse path.

Regardless of which path you choose, the one constant is testing. When using vendor-prefixed properties you’re leveraging early, often unstable functionality that can change even after a browser first introduces support for a property, so it’s critical to test with each browser update to make sure your site functions as expected.

—John Hrvatin, Lead Program Manager, Internet Explorer

Comments

  • Anonymous
    October 28, 2011
    This is such a helpful pattern. thanks!!

  • Anonymous
    October 28, 2011
    Why are you setting styles directly in javascript anyway?  You should keep the styles in CSS (with all its prefixed awesomeness) under a class, and then just switch the className. Amateurs.

  • Anonymous
    October 28, 2011
    Poor KHTML, always left out. :(

  • Anonymous
    October 29, 2011
    Sadly, I have been unable to use WebGL on IE10 yet, what is the vendor prefix to access it?

  • Anonymous
    October 29, 2011
    2 Humbug: do not worry, learn and one day you may understand.

  • Anonymous
    October 29, 2011
    Yeah you are an ammature and idiot as well .. just saying

  • Anonymous
    October 29, 2011
    Modernizr has been mentioned before here. It's worth pointing out that FirstSupportedPropertyName is pretty much available in Modernizr. The API goes like.. Modernizr.prefixed('transform') // 'msTransform' One thing this article does explicitly say is that while Webkit, Moz, and O prefixes use a capital letter on their prefixed style properties, IE retains a lowercase ms. A little tricky and unexpected (oh and Webkit is also available with lowercase, but that's just for convenience)

  • Anonymous
    October 30, 2011
    Paul Irish, of Google. The Microsoft guys always identify themselves as such. Why don't you?

  • Anonymous
    October 30, 2011
    The guy who annoys me to no end with his practice of using @someone as name, you mean to tell me you don't know who Paul Irish is? Then what are you doing here reading this blog which happens to be about web development? Here's a clue for you: www.youtube.com/watch

  • Anonymous
    October 30, 2011
    Microsoft please release a version of IE10 for windows 7 soon... i don't want to wait to December or September of 2012 to see IE10 released for windows 7..

  • Anonymous
    November 01, 2011
    The comment has been removed

  • Anonymous
    November 08, 2011
    Two returns in one function? Many frown on that. You can break the loop and return the first found non-undefined property instead.

  • Anonymous
    November 09, 2011
    The comment has been removed

  • Anonymous
    November 04, 2013
    No wonder why IE is totally broken and will never be even close to be modern.