O'Reilly

Detect Vendor Prefix with JavaScript

By on  

Regardless of our position on vendor prefixes, we have to live with them and occasionally use them to make things work.  These prefixes can be used in two formats:  the CSS format ("-moz-", as in -moz-element) and the JS format ("navigator.mozApps").  The awesome X-Tag project has a clever bit of JavaScript magic that detects those prefixes in the browser environment -- let me show you how it works!

The JavaScript

The first step is retrieving the HTML element's CSSStyleDeclaration:

var styles = window.getComputedStyle(document.documentElement, ''),

The next step is converting it to an Array object and searching for a known prefix type, settling on Opera if none is found:

pre = (Array.prototype.slice
      .call(styles)
      .join('') 
      .match(/-(moz|webkit|ms)-/) || (styles.OLink === '' && ['', 'o'])
    )[1]

With the CSS prefix found, getting the JS-formatted prefix is simple:

return {
    dom: dom,
    lowercase: pre,
    css: '-' + pre + '-',
    js: pre[0].toUpperCase() + pre.substr(1)
  }

The returned object provides an object that looks something like:

Object {dom: "WebKit", lowercase: "webkit", css: "-webkit-", js: "Webkit"}

A complete representation of the vendor prefixing for the host browser. Here's the complete snippet:

var prefix = (function () {
  var styles = window.getComputedStyle(document.documentElement, ''),
    pre = (Array.prototype.slice
      .call(styles)
      .join('') 
      .match(/-(moz|webkit|ms)-/) || (styles.OLink === '' && ['', 'o'])
    )[1],
    dom = ('WebKit|Moz|MS|O').match(new RegExp('(' + pre + ')', 'i'))[1];
  return {
    dom: dom,
    lowercase: pre,
    css: '-' + pre + '-',
    js: pre[0].toUpperCase() + pre.substr(1)
  };
})();

Grabbing the CSSStyleDeclaration from the HTML element is a clever move.  This method plays off of the fact that there will always be a vendor-prefixed property in the style declaration, which some may dislike, but is going to be effective for a long time to come.  What do you think of this method of vendor prefix detection?  Share your thoughts!

O'Reilly Velocity Conference
Save 20% with discount code AFF20

Recent Features

Incredible Demos

  • MooTools History Plugin

    One of the reasons I love AJAX technology so much is because it allows us to avoid unnecessary page loads.  Why download the header, footer, and other static data multiple times if that specific data never changes?  It's a waste of time, processing, and bandwidth.  Unfortunately,...

  • Create a Spinning, Zooming Effect with CSS3

    In case you weren't aware, CSS animations are awesome.  They're smooth, less taxing than JavaScript, and are the future of node animation within browsers.  Dojo's mobile solution, dojox.mobile, uses CSS animations instead of JavaScript to lighten the application's JavaScript footprint.  One of my favorite effects...

Discussion

  1. Maybe I’m not getting this right, but as far as I’ve played with manipulating properties (that still need a prefix) via JavaScript, I was under the impression that:
    – both webkit and Webkit work for WebKit browsers;
    ms works in IE, but not Ms;
    Moz and O work, but not moz or o (Opera also recognizes Webkit, but not webkit)

    link to test pen“>http://codepen.io/thebabydino/pen/HKstJ” rel=”nofollow”>link to test pen

    link to test pen" data-user="thebabydino">
  2. Hi David,

    Is there a way i can find is a css property value is supported by a particular browser. For ex: Android browser < 2.3 does not support overflow:auto or scroll. Is there a way i can find if the value is supported by a browser? i tried with javascript all i could find using document.body.style.propName but not for the property:value support.

    Thanks

    • That’s particularly what should do @support in the future.

      You could dig into Modernizr.testStyle and check the element property. Like with:

      Modernizr.testStyle('#dummy{ overflow: visible; overflow: scroll }', function(element){
        Modernizr.addTest('overflow', element.style.overflow === 'scroll');
      });

      If the browser can’t support scroll, I guess it will stick on visible.

    • I’m not sure, but i think you can create element, then apply desired value to property you want and check if it’s there :)
      Like this:

      function check (property, value) {
      
          var shadow = document.createElement('div');
      
          // Checking if we can even use this property
          if (!(property in shadow.style)) {
              return false;
          }
      
          // Applying desired value
          shadow.style[property] = value;
      
         // If the property is equal to the desired -> you can use it
          if (shadow.style[property] == value) {
              return true;
          }
      
          // Sadly, you can't use it :(
          return false;
      }
      
      check('display', 'none') // TRUE;
      check('display', 'hidden') // FALSE
      
    • Deepak

      Thanks everyone :)

  3. Aicke Schulz

    @David Walsh: Well done!

    @Deepak David: If you are looking for a generic approach look at Modernizr.

    p.s.: I know it is not the problem of this solution, I just want to mention, that in older browser versions the css style name can differ from the official / non-prefixed style name, e.g. -moz-border-radius-topright vs. border-top-right-radius. But this problem will fade away with time.

  4. It sure is an interesting way to detect style-prefixes.
    But why not detect it depending on userAgent string? Webkit -> webkit, Opera -> O, Mozilla -> Moz and IE -> ms ?
    And why store so many useful information u will never need like {dom: "WebKit", lowercase: "webkit", css: "-webkit-", js: "Webkit"}?

    I see the only reason to know a proper vendor prefix is for function that applies styles to the elements.
    Something like this:

    applyCSS = (function () {
    
        var prefix = getVendorPrefix(); // function, that detects prefix using UA string
    
        return function (element, styleName, styleValue) {
    
            // Collecting right style name
            var realStyleName = getStyleName();
    
            // Do nothing if there's no right style name
            if (!realStyleName) return;
    
            // Applying style
            element.style[realStyleName] = styleValue;
    
            /**
             * This function returns proper style name if there is one
             */
            function getStyleName () {
    
                var prefixedStyleName;
    
                // If there's no need for prefix
                if (styleName in element.style) {
                    return styleName;
                }
    
                // Creating style with vendor prefix
                prefixedStyleName = prefix + styleName.slice(0,1).toUpperCase() + styleName.slice(1);
    
                // Checking again
                if (prefixedStyleName in element.style) {
                    return prefixedStyleName;
                }
    
                // Browser has no support for this style. Shame! :)
                return false;
            }
            
        };
    
    
        /**
         * Returns proper vendor prefix name
         */
        function getVendorPrefix () {
        
            var ua = navigator.userAgent.toLowerCase(),
                match = /opera/.exec(ua) || /msie/.exec(ua) || /firefox/.exec(ua) || /(chrome|safari)/.exec(ua),
                vendors = {
                    opera: 'O',
                    chrome: 'webkit',
                    safari: 'webkit',
                    firefox: 'Moz',
                    msie: 'ms'
                };
            
            return vendors[match[0]];
        }
    
    })();
    

    Sorry My English.

  5. String indexing (e.g. pre[0]) is not supported by IE < 8. (As a side note, IE8 itself supports it for string literals only, not for string objects.)

    Also, substr is a non-standard String method. Why not just use slice or substring instead?

    • IE8 and down isn’t supported by x-tag, so that isn’t in the realm of desire. As for substr, I’ll pass that on.

  6. Internet Explorer supports prefix-less properties. So you should probably something more like this is appropriate in the modern world.

    MBP.getVendorPropertyName = function (prop) {
    
        var prefixes = ['Moz', 'Webkit', 'O', 'ms'],
                    vendorProp, i,
                    div = document.createElement('div'),
                    prop_ = prop.charAt(0).toUpperCase() + prop.substr(1);
    
        if (prop in div.style) {
            return prop;
        }
    
        for (i = 0; i < prefixes.length; ++i) {
    
            vendorProp = prefixes[i] + prop_;
    
            if (vendorProp in div.style) {
                return vendorProp;
            }
    
        }
    
        // Avoid memory leak in IE.
        this.div = null;
    };
    
  7. Hi David, thanks very much for sharing your efforts in making a JS function that tries to detect the vendor. I will try to use our code in the WP framework we’re working on. We generate CSS on client side (kind of like LESS does, but more simplistic) and I was looking for ways to minimize the CSS generated by our JS (in case you’re interested, see https://github.com/nexusthemes/nexusframework). In our framework the JS produces 779 CSS selectors. Injecting the CSS takes around 60 msecs on desktop (which is fair), but about 330 msecs on my ipad 1 in Chrome. I don’t know if lowering the amount of CSS will also lower the time it takes to process the CSs, but anything I can do to lower the 330 msecs sounds like worth investing :) Using your function I hope to reduce 6 lines of CSS;

    // helper function to get the css output for a lineair gradient between the 2 specified colors
    function nxs_js_getlineairgradientcss(colora, colorb)
    {
    	var result = "";
    	result += "background-color: " + colora + ";";
    	result += "background: -o-linear-gradient(" + colorb + ", " + colora + ");";
    	result += "background: -moz-linear-gradient(" + colorb + ", " + colora + ");";
    	result += "background: -webkit-linear-gradient(" + colorb + ", " + colora + ");";
    	result += "background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(" + colorb + "), to(" + colora + "));";
    	result += "filter: progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=" + colorb + ",EndColorStr=" + colora + ");";
    	return result;
    }
    

    Into just 1 or 2 lines.

    Anyways, thanks again.

  8. I’ve been using the snippet below in some of my jQuery plugins. How does it compare to your method?

    var prefix = (/mozilla/.test(navigator.userAgent.toLowerCase()) && 
                 !/webkit/.test(navigator.userAgent.toLowerCase())) ? '-moz-' : 
                 (/webkit/.test(navigator.userAgent.toLowerCase())) ? '-webkit-' :
                 (/msie/.test(navigator.userAgent.toLowerCase()))   ? '-ms-' :
                 (/opera/.test(navigator.userAgent.toLowerCase()))  ? '-o-' : '';
  9. Nice article.

    How would one not use the the prefix, say, if the browser is able to run something prefix free?

    For example, an up-to-date Chrome doesn’t need

    -webkit-

    for transform.

    thanks

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!

Recently on David Walsh Blog

  • Open Files from Command Line on OS X

    I'm as much of a fan of application UIs as anyone else but I'm finding myself working more and more from the command line lately.  Much of that is becoming obsessed with media manipulation but I'm forcing myself to use less UIs so that I...

  • Get Stock Quotes From Command Line

    When I conned my way into my first professional programming gig, I didn't really think much about money -- just that I was getting my foot in the door.  But as my career has gone on, I've been more aware of money, investing, and retirement.  I've recently...

  • Geolocation API

    One interesting aspect of web development is geolocation; where is your user viewing your website from? You can base your language locale on that data or show certain products in your store based on the user's location. Let's examine how you can...

  • Create an Image Preview from a Video

    Visuals are everything when it comes to media.  When I'm trying to decide whether to watch a video on Netflix, it would be awesome to see a trailer of some kind, but alas that isn't available.  When I'm looking to download a video on my computer,...

  • New:  Webdesigner News!

    A new and exciting website has recently been launched for web designers and developers. You likely spend hours every morning browsing through hundreds of posts on your RSS feeds, hoping to stumble across relevant stories. Webdesigner News was built to provide web designers and developers with...