Tutorials Books Videos Forums

Change the theme! Search!
Rambo ftw!

Customize Theme


Color

Background


Done

Vendor Prefixes and JavaScript

by kirupa   |   9 December 2012

A necessary annoyance when building your HTML5 content, especially content using cutting edge features, is the need to use vendor prefixes. If you've ever had to write duplicate lines of CSS with a property prefixed by -ms, -moz, -o, and -webkit, then you know all too well what I am talking about.

If you are not familiar or need something to jog your memory, below is an example of a style rule that uses the brand new transform CSS property:

.foo {
	transform: skewx(20deg) translatex(100px);
	-ms-transform: skewx(20deg) translatex(100px);
	-moz-transform: skewx(20deg) translatex(100px);
	-o-transform: skewx(20deg) translatex(100px);
	-webkit-transform: skewx(20deg) translatex(100px);
}

Because this property is really new and supported only at an experimental level by browsers, we need to access the experimental version of the property that is designated with a vendor prefix. You can see these prefixes in my use of the transform property above. You'll see equivalent variations for other CSS3 properties besides just transform as well. There is a whole lot of properties that you may end up having to use vendor prefixes on.

While you often think about CSS when fiddling with vendor prefixes, the need to use vendor prefixes goes beyond it. JavaScript is affected as well. If you write any JavaScript that accesses the vendor prefixed CSS properties or uses some cutting-edge API like requestAnimationFrame or indexedDb, you need to call back the vendor prefixes bandwagon. Just like with CSS, if you don't use a vendor prefix and are running your content in a browser that is looking for a prefixed property or function, your script will fail.

Nobody will argue that vendor prefixes are fun to write. They result in duplicate or unnecessary lines of code which you generally want to avoid. Now, there are ways to deal with vendor prefixes in your CSS. You could use a library like -prefix-free and bypass them altogether. When it comes to JavaScript, though, you aren't quite that lucky. There aren't too many shortcuts or ways to work around using vendor prefixes. The best we can do is reduce the inconvenience.

That's where this tutorial comes in. It will help you minimize the pain of having to deal with vendor prefixes in JavaScript. Who knows, maybe you may even enjoy it!

Onwards.

Meet the Vendor Prefixes in JavaScript

Let's take stock of who the main actors in this JavaScript themed tragedy are. You have four vendor prefixes that you have to deal with:

Note that the prefixes for use in JavaScript are a bit different than what you have in CSS. For example, the CSS prefix for Mozilla is -moz, but it is just moz in JavaScript.

Dealing with Vendor Prefixes

Here is what you need to remember when working with vendor prefixes. Only one of them actually matters. The "one" depends on the browser that is currently being used. To look at this further, let's just pick on requestAnimationFrame for now. This is a new function that is still undergoing some changes, so using it will require vendor prefixes. Remember, you can't skimp on vendor prefixes. You need to specify them all. Internet Explorer will only care about msRequestAnimationFrame. Chrome will only care about webkitRequestAnimationFrame. A similar story will play for the Firefox and Opera equivalents of this function.

The challenge for you is to provide a simple way to deal with each vendor prefix without bloating your code excessively. The approach you use for dealing with them depends on whether what you are trying to access is a function or a CSS property. We'll look at both, for the solution to each is quite different.

Vendor Prefixes and Functions

When you are trying to access functions that require vendor prefixing, the way to solve this is actually pretty simple. It involves using the OR operator (||) operator, each vendor-prefixed version of the function you are trying to access, and a variable. Let's walk through what this would look like for requestAnimationFrame.

The code looks as follows:

var requestAnimationFrame = window.requestAnimationFrame || 
							window.mozRequestAnimationFrame || 
							window.webkitRequestAnimationFrame || 
							window.oRequestAnimationFrame || 
							window.msRequestAnimationFrame;

I have a variable called requestAnimationFrame. The value it stores is a result of evaluating that OR expression. This value could be one of two things. It could be the appropriate requestAnimationFrame function, or it could be undefined. As OR expressions go, just one of the conditions needs to evaluate to true in order for the entire expression to become true. As long as one of our window.*requestAnimationFrame conditions exists, the entire expression is true and your requestionAnimationFrame variable gets the value of the function that saved the day.

If the script is being run on an older browser that doesn't support requestAnimationFrame at all, then the value for the requestAnimationFrame variable will just be undefined. That is because every condition in the expression will return false or undefined. No amount of fiddling with vendor prefixes will return anything otherwise, so you need to properly ensure your code handles such cases gracefully.

Vendor Prefixes and CSS Properties

Accessing functions that require vendor prefixes is one thing. As you will see in this section, accessing the new CSS properties using JavaScript is a bit different. It requires you checking whether the style object has a property containing the CSS property name you are looking for. That involves a little bit of extra code.

The best way to explain it all is just to take a look at an example. For this, I am going to pick on the transform CSS property that I kind of let off easy in the introduction.

The code for using this property in JavaScript looks as follows:

function getSupportedPropertyName(properties) {
	for (var i = 0; i < properties.length; i++) {
		if (typeof document.body.style[properties[i]] != "undefined") {
			return properties[i];
		}
	}
	return null;
}


var transform = ["transform", "msTransform", "webkitTransform", "mozTransform", "oTransform"];

var item = document.querySelector("#theItem");
var transformProperty = getSupportedPropertyName(transform);

if (transformProperty) {
	item.style[transformProperty] = "rotate(45deg)";
}

Take a few moments and (while pretending that you have an element whose id is theItem) walk through the code and see how this would all work. In the next section, I'll try to explain everything.

The Code Explained

What our code does is simple. In an ideal world that does not have any vendor prefixes, I would simply set the transform property in JavaScript as follows:

myObject.style.transform = "rotate(45deg)";

In the non-ideal (aka real) world, the problem is the transform property. Not all browsers can understand it. What they can understand is the vendor-prefixed equivalent such as msTransform, webkitTransform, mozTransform, and oTransform.

So, what we need is pretty simple. We need to somehow ensure the style object accesses the correct "transform" property that works on the browser that is currently trying to access it. That's it. That's all we have to do. That's all the code that you see does.

Crucial to this is the array of the vendor-prefixed properties:

var transform = ["transform", "msTransform", "webkitTransform", "mozTransform", "oTransform"];

I have a variable called transform that stores every variation of the transform property along with its vendor-prefixed friends. This array gets passed in to the getSupportedPropertyName function:

function getSupportedPropertyName(properties) {
	for (var i = 0; i < properties.length; i++) {
		if (typeof document.body.style[properties[i]] != "undefined") {
			return properties[i];
		}
	}
	return null;
}

This function's job is to go through each item in the array and ask "Will you work?" The way this gets answered is by checking if the property name in the array actually exists:

if (typeof document.body.style[properties[i]] != "undefined") {
	return properties[i];
}

That's the job of this chunk of code. If the typeof evaluation doesn't return undefined, then the property whose name matches the item (properties[i]) in our array actually exists. If it exists, then we can use it for real. If none of the property names we specified in the array exist, then the getSupportedPropertyName function returns a null.

The code for handling the null case and setting the right property looks as follows:

var item = document.querySelector("#theItem");
var transformProperty = getSupportedPropertyName(transform);

if (transformProperty) {
	item.style[transformProperty] = "rotate(45deg)";
}

That's all there is to it. While this example was very focused on the transform property, this all works just as well for any other CSS3 property that you want to access via JavaScript. Just make sure the array you are passing into getSupportedPropertyName contains the vendor prefixed variations spelled (and capitalized) correctly.

Conclusion

If we are lucky (and the world doesn't end towards the middle of December 2012), the properties and functions we need to set vendor prefixes on will slowly be going away. Even while writing this article, about half of the browsers worked without requiring a vendor prefix for either requestAnimationFrame or transform, so that is certainly a good thing. Of course, that doesn't mean there aren't people in some dark dungeon somewhere coming up with new properties that, when unleashed upon the world, will initially require their own level of vendor prefix support.

Just a final word before we wrap up. If you have a question and/or want to be part of a friendly, collaborative community of over 220k other developers like yourself, post on the forums for a quick response!

Kirupa's signature!

The KIRUPA Newsletter

Thought provoking content that lives at the intersection of design 🎨, development 🤖, and business 💰 - delivered weekly to over a bazillion subscribers!

SUBSCRIBE NOW

Creating engaging and entertaining content for designers and developers since 1998.

Follow:

Popular

Loose Ends

:: Copyright KIRUPA 2024 //--