Polyfilling Doesn’t Have to be Difficult

In 2010 Remy Sharp published an article on his blog where he introduced a new word: polyfill. A polyfill, as he wrote, is a piece of code (or plugin) that provides the technology that you, the developer, expect the browser to provide natively. In the same post he explained why he coined this word:

Earlier on this year I co-authored “Introducing HTML5” with Bruce Lawson. During the R&D time I was looking at shims and techniques to plug missing APIs but they weren’t quite progressive enhancement. I wanted a single word that represented this idea that there was a ‘thing’ that could plug browsers but wasn’t progressive enhancement but wasn’t graceful degradation either.

To many of you reading this article the term should not sound new and more probable than not you’re well accustomed to it. For those that aren’t very familiar with it, you can think to a polyfill as an arbitrary pieve of complex code that mimics a feature not yet supported by a given browser.

In this article I’ll discuss two examples of features, one introduced in ECMAScript 6 and the other in HTML5, for which I’ll build the relavant polyfill so that these features can be used also in browsers that don’t support them natively.

Number.isInteger(): A simple example

One of the simplest examples I can think of that needs a polyfill is the Number.isInteger() method. This method is new to JavaScript as it belongs to version 6 of the ECMAScript standard. This method tests whether a given value passed as a parameter is an integer. An example of use is shown below:

var num = 10;
var floatNum = 13.4
var notNum = 'test';
// prints true
console.log(Number.isInteger(num));
// prints false
console.log(Number.isInteger(floatNum));
// prints false
console.log(Number.isInteger(notNum));

The problem with this method is that it’s supported in the latest versions of Chrome, Opera, and Firefox but not in Internet Explorer or Safari. What this means is that if you’re using this method in your website, as soon as a user browsing with Internet Explorer or Safari visits the website, an error will be generated because the method isn’t defined. As a consequence of the error, all the code after that statement won’t be executed. Using a try...catch block you can bypass this issue but you still don’t have a native method to test if a given value is an integer or not. This is exactly where a polyfill comes in handy.

Many developers think that developing a polyfill is over their head. But this isn’t true. Most polyfills aren’t very complex (some are only 5 lines of code and you don’t need to recreate Windows in JavaScript) but they require a bit of imagination, patience, and skills. Imagination is needed because for some features you’ll need to think of a creative way to reproduce, or mimic, the feature in your polyfill.

To see what I mean, let’s try to develop a polyfill for the isInteger() method.

Creating the polyfill

In order to create a polyfill, you have to gather all the information about the feature you want to reproduce. The isInteger() method accepts only one parameter and returns true if the passed value is an integer; false otherwise. Before we delve into the development of the polyfill, it’s important to remember that JavaScript is a loosely typed language, so equivalences like the following are all true:

1 == 'test'
true == 1
'1' == true

This is important as we explore how to build the polyfill.

To create our polyfill we have to think about the difference between an integer and a floating number. Intuitively the difference is simple but putting it into code might not be immediately obvious. So, let’s list a few numbers to try to visualize the difference from a mathematical point of view:

  • 10
  • 23.5
  • 6.342
  • 6

A floating number is an integer with a fractional part, or from a different perspective, an integer is a floating number with a fractional part equal to zero. So, to verify if a number is an integer we should be able to check that the fractional part is equal to zero. To do that, we can subtract the integer portion from any number and test whether the result is equal to zero. This can be achieved by subtracting the number from itself using the Math.floor() method, and verifying if the result of the subtraction is zero as in the following function:

function isInteger(number) {
   return number - Math.floor(number) === 0;
}

This is a good start but what if the caller of this function passes a value that isn’t a number like null or an array? To solve this issue we have to test if the given value is actually a Number. Adding this change to our function results in the following code:

function isInteger(number) {
   return typeof number === 'number' && number - Math.floor(number) === 0;
}

So far we have built a function that does what we need but it’s not a native method. We can’t call our isInteger() function as Number.isInteger(). The next step is to attach our function to the Number data type:

Number.isInteger = function(number) {
   return typeof number === 'number' && number - Math.floor(number) === 0;
};

There is one last issue remaining. If we include the prior code, every browser accessing the website will execute it. As a result if the browser supported the method natively, the latter will be overridden by our definition, something we want to avoid. To solve this issue we can test for the presence of the method and, if the method doesn’t exist, we attach our function.

There are two ways in which we can perform this task. The first is to wrap the statement inside an if:

if (!Number.isInteger) {
   Number.isInteger = function(number) {
      return typeof number === 'number' && number - Math.floor(number) === 0;
   };
}

The second one, which is the one I prefer, consists of using the OR operator:

Number.isInteger = Number.isInteger || function (number) {
   return typeof number === 'number' && number - Math.floor(number) === 0;
};

There we go! A completely working polyfill that we can use in our code… almost. This polyfill works in most cases but it wasn’t created according to those annoying long descriptions known as specifications. The complete polyfill for the Number.isInteger() method taken from the relative page of the Mozilla Developer Network looks like:

if (!Number.isInteger) {
  Number.isInteger = function isInteger(nVal) {
    return typeof nVal === 'number'
      && isFinite(nVal)
      && nVal > -9007199254740992
      && nVal < 9007199254740992
      && Math.floor(nVal) === nVal;
  };
}

Although its slightly more complex than the one we created, the point here is that building a polyfill isn’t rocket science and even a beginner developer can develop one.

The placeholder attribute: A more complex case

Some of you may think that I’ve selected a very simple example that doesn’t represent the reality of how complex it is to build a polyfill. You might think that creating a polyfill for something more complex like a native visual feature would be too difficult to achieve on your own. In this section, I’ll try to prove that this is wrong by explaining how to develop a polyfill for the placeholder attribute. Once again, I want to stress that apart from some exceptions, developing a polyfill is just a combination of imagination, patience, and skills.

In this section I won’t cover all the details as I did in the previous example, but I’ll provide you with enough detail to make the point.

Developing the polyfill

The placeholder attribute is used on input fields and specifies text to show inside the field until the field gains focus, when the text is hidden. Another important detail about the placeholder attribute is that the color of the text shown inside the fields is grey and corresponds to the HEX value A9A9A9.

Although there are many techniques that you can employ to create a polyfill for the placeholder attribute, I’ll discuss the one that uses the value attribute to mimic the feature. When the page loads, we’ll copy the value of the placeholder attribute into the value attribute. This is done using the following code:

var inputsWithPlacehoder = document.querySelectorAll('input[placeholder]');
for (var i = 0; i < inputsWithPlacehoder.length; i++) {
   inputsWithPlacehoder[i].setAttribute('value', inputsWithPlacehoder[i].getAttribute('placeholder'));
}

To reproduce the placeholder’s color we can create a class like the following:

input.fake-placeholder
{
   color: #A9A9A9;
}

You may ask, “Why use a class?” The reason is that your site may have defined a color for form field text, for example green. In this case, the text of the field needs to be grey when the placeholder is in place, but green when the field contains text written by the user. To do this, we toggle the fake-placeholder class.

Let’s move ahead and discuss the events we need to listen for. The normal behavior for a placeholder is to disappear as soon as the user types the first character and, since the simulated placeholder is a real text value, the user expects the caret to be placed at the beginning of the placeholder’s text itself. Therefore, we need to listen for the following four events:

  • focus: If the field has the placeholder set and the user hasn’t typed anything, we’ll move the caret to the beginning of the placeholder’s text.
  • keydown: Remove the placeholder and restore the natural color of the text.
  • keyup: If the value is empty, restore the placeholder and the grey color.
  • blur: If the value is empty restore the placeholder and the grey color.

For example, to move the caret to the beginning of the placeholder’s text we have to add the following function as a listener for the focus event:

function moveCaret() {
   if (this.value === this.getAttribute('placeholder')) {
      if (this.createTextRange) {
         var part = this.createTextRange();
         part.move('character', 0);
         part.select();
      } else if (this.setSelectionRange) {
         this.setSelectionRange(0, 0);
      }
   }
}

Note: The actual code is a bit more complex because of the Chrome issue #32865. If you want to read the full code needed, take a look at the source of my jQuery plugin Audero Unified Placeholders. I mentioned patience was needed when developing a polyfill, haven’t I?

By using this information you should now be able to create your own polyfill of the placeholder attribute, but, if you’re looking for a complete guide, I wrote an article titled How to create an advanced HTML5 placeholder polyfill that you can read.

Other examples of polyfills

Polyfills can be as simple or as complex as you want and usually the amount of code needed depends on the feature you’re polyfilling. Nowadays there are thousands polyfills and you can find a good list in the Modernizr repository under HTML5 Cross Browser Polyfills. This page lists all the shims, fallbacks, and polyfills to implant HTML5 functionality in browsers that don’t natively support them. Some examples taken from this list are:

Conclusions

In this article I’ve introduced you to the concept of polyfilling and how, with time, imagination and skills, you can create your own to support features you love and need in browsers that don’t provide these features natively. We looked at a simple example showing how to develop a polyfill for the Number.isInteger() method introduced in ECMAScript 6. Then, we discussed how to create a polyfill for the placeholder attribute introduced in HTML5.

I hope you enjoyed this article and that you’ll find that polyfills aren’t scary and that you might consider creating one for yourself.

Header image courtesy of Sarah Reid

Comments

  • Peter

    A good solution https://cdn.polyfill.io/v1/docs/
    In many projects i use some polyfills from moz-dev, but now i use the lib and forget the crappy old browsers and the hacks with polyfills.

    • Aurelio De Rosa

      Hi Peter.

      A service like the one you linked is ideal when you have to integrate polyfills in your website but in this article I tried to push a different idea. Specifically, I wanted to show that building your own, even for educational purposes only, isn’t difficult and anyone can try.