Polyfill for Reversed Attribute on Lists

Lists in HTML typically appear in decimal fashion, starting from 1 and going to n. Sometimes these are instructed to appear in reversed order via the reversed boolean attribute on the ol element.

<ol reversed>
    <li>I am number two.</li>
    <li>And I am number one.</li>
</ol>

Some browsers don’t understand the reversed attribute, and thus will not reverse the list. Although you cannot reverse the entire list in some browsers with this attribute, you can manually override the value associated with each list item in most browsers via the value attribute.

<ol>
    <li value="2">I am number two.</li>
    <li value="1">And I am number one.</li>
</ol>

Using jQuery, we can quickly create a short polyfill that will give us reversed functionality in browsers where it’s not natively understood.

(function () {
    if ( $("<ol>").prop("reversed") === undefined ) {
        $("ol[reversed]").each(function () {
            var $items = $(this).find("li");
            $items.attr("value", function ( index ) {
                return $items.length - index;
            });
        });
    }
}());

The code should be fairly straight-forward, but let me explain what is going on just to be on the safe side.

We start with an IIFE (Immediately Invoked Function Expression) which runs our code instantly, as well as keeps all declared variables out of the global namespace.

Next we kick things off with a quick feature-test on a freshly-made ol. In browsers that understand the reversed property, the default value is false. In other browsers, the property is undefined.

We then grab a collection of all lists that have the reversed attribute. Next we cycle over this collection of lists, assigning the list items from each iteration to the variable $items.

We then take the $items jQuery collection and begin an implicit loop setting the value attribute of each item in the collection to collection.length - itemIndex. If there are five items in the list, and we’re on index 0 (first item), five will be returned as the value. When we enter the second iteration, and the index is 1, (5-1) will be returned, resulting in a value of 4.

Voila, we have polyfilled reversed. There are some shortcomings; this won’t work on lists created after this code runs. You could however put this logic into a function declaration and call it after the DOM has been updated asynchronously, so as to handle any new lists that might have populated the document.

One very important note when using jQuery as a means to polyfill older browsers, you will have to use jQuery 1.8.3 or below. Around the release of jQuery 1.9, a lot of antiquated code was removed that once propped up older browsers. jQuery 1.9 and forward are meant for modern browsers, and as such they will fail in carrying your polyfilled functionality back to older browsers like Internet Explorer 6, and its contemporaries.

Creating a (autofocus) Polyfill

In the ever-changing world of browser topography, every so often there exists an expectation in the community that some feature or API will be available in a browser, and it’s not.

We have an approach to mitigating these types of issues though, and we call it polyfilling. To quote Remy Sharp, “A polyfill, or polyfiller, is a piece of code (or plugin) that provides the technology that you, the developer, expect the browser to provide natively. Flattening the API landscape if you will.”

A question was recently asked on Stack Overflow about automatically applying focus to an input element when the page loads. The first responses suggested using a tool like jQuery to target the element, and invoke its focus method. While this works, it adds additional overhead in environments where it’s not needed.

My initial solution to the question was to use the autofocus boolean attribute on the input element of choice. Shortly after suggesting this, I realized this attribute was only introduced in Internet Explorer at version 10, meaning users of 6, 7, 8, and 9 wouldn’t get the behavior the developer expected – perfect opportunity to author a polyfill.

Our goal in building a solid polyfill is to not make its presence known; we want the web developer to author their markup as they would normally, while we quietly teach the browser to make sense of the unknown autofocus attribute.

So we start with basic HTML:

<input name="name" placeholder="John Doe" autofocus />

In browsers like Internet Explorer 10, Chrome 26, or Firefox 20, focus will be immediately given to this element upon page load. But for Internet Explorer 6-9 and versions of Firefox prior to 4.0, we’ll have to add a bit of assistance.

We start by first performing a feature-detect for the autofocus property in a freshly-created input element. In browsers that support this property, the default value is false:

// Proceed only if new inputs don't have the autofocus property
if ( document.createElement("input").autofocus === undefined ) {

}

This condition will only evaluate to true in browsers that don’t natively understand the autofocus attribute, and as such, this will be our sandbox to play in while constructing our polyfill. We’ll need to go through a few steps for this polyfill, so let’s have a look at what they are:

  1. Cycle over all forms
  2. Cycle over all elements in each form
  3. Check attributes of each element for autofocus
  4. If found, trigger focus, and break out of all loops (only one instance of autofocus should exist on in a document, otherwise you’ll get inconsistent cross-browser results)

Our first step in the above list is to cycle over all the forms. We can use the document.forms collection to find all of the forms on the document. We’ll also create a variable to track our index in the collection of forms. Lastly, we’ll add a label to this loop so that we can break from it later:

// Get a reference to all forms, and an index variable
var forms = document.forms, fIndex = -1;

// Begin cycling over all forms in the document
formloop: while ( ++fIndex < forms.length ) {

}

Next, within our formloop, we need to get a collection of the current form’s elements. Additionally, we will be creating another index variable to help us navigate this new collection:

// Get a reference to all elements in form, and an index variable
var elements = forms[ fIndex ].elements, eIndex = -1;

// Begin cycling over all elements in collection
while ( ++eIndex < elements.length ) {

}

The third step in our process is to check for the autofocus attribute in each element. There are various ways you can do this, and I explored several of them. For instance, you could use the hasAttribute method, but this is only supported in Internet Explorer from version 8 (meaning you’d have to polyfill it in earlier versions).

In the end I wound up checking for the attribute as a key in the element.attributes collection. If not present, the result will be falsey, and the loop will skip to the next element in the nodeList:

// Check for the autofocus attribute
if ( elements[ eIndex ].attributes["autofocus"] ) {

}

Finally, we arrive at our last step. We have found an element that has the attribute we’re looking for, and it’s time to focus on it, break out of the loops, and call it quits.

// If found, trigger focus
elements[ eIndex ].focus();

// And break out of outer loop
break formloop;

This is where the formloop label comes in handy; a simple break would have broken the element loop, but not the forms loop, meaning subsequent form elements (and their children) would be evaluated. Since only one instance of autofocus should appear in a document, this is not the behavior we want.

The final result is wrapped in an IIFE (Immediately Invoking Function Expression) which runs our code immediately, as well as prevents our variables from appearing in the global namespace, potentially causing harm to the rest of the document or application.

(function () {

    // Proceed only if new inputs don't have the autofocus property
    if ( document.createElement("input").autofocus === undefined ) {

        // Get a reference to all forms, and an index variable
        var forms = document.forms, fIndex = -1;

        // Begin cycling over all forms in the document
        formloop: while ( ++fIndex < forms.length ) {

            // Reference all elements in form, and an index variable
            var elements = forms[ fIndex ].elements, eIndex = -1;

            // Begin cycling over all elements in collection
            while ( ++eIndex < elements.length ) {

                // Check for the autofocus attribute
                if ( elements[ eIndex ].attributes["autofocus"] ) {

                    // If found, trigger focus
                    elements[ eIndex ].focus();

                    // And break out of outer loop
                    break formloop;

                }

            }

        }

    }

}());

There you have it, a polyfill that gives autofocus behavior to browsers that don’t natively support it. By dropping this in to your website, you can now carry on your merry way using great features of HTML like this, without worrying what negative effect it might have on your Internet Explorer 6, 7, 8, or even 9 visitors.