Progressive Enhancement Javascript Utility Classes

With the last few websites I have made, I have found that getting the site to work without Javascript is generally the last thing I do. I know this is wrong, so this time I am going to correct it by planning for the absence of Javascript from the beginning. My usual form of progressive enhancement is putting special classes on the elements and then adding and removing various classes at the top of the Javascript file. For my new project I decided to improve upon the process, because my current style ends up with a lot of sloppily overriding styles, and I have having to use !important. So I have created a short snippet of Javascript that knows when to toggle classes based on another class. Here is a simple demo. When viewed with Javascript the top box is green, and when viewed without Javascript the bottom box is red.

How Does This Work?

Here is the HTML:

<div class="box only-js-green"></div>
<div class="box no-js-red red"></div>

The box class is just default styling. Any class of the form .only-js-x will add class .x to the element when Javascript is enabled. Any class of the form .no-js-x will remove class .x from the element when Javascript is disabled. The only requirement of the HTML is that for class .no-js-x to work, class .x must be part of the element.

Now for the JQuery (don't worry, there isn't too much of it)

$(document).ready(function() {
  $("div[class^='only-js-'],div[class*=' only-js-']").each(function() {
    var str = $(this).attr('class');
    var classNames = str.split(" ");
    for (var i = 0; i < classNames.length; i++) {
      if(classNames[i].substring(0, 8) == 'only-js-') {
        $('.' + classNames[i]).addClass(classNames[i].substring(8));
        }
      }
    });
  $("div[class^='no-js-'],div[class*=' no-js-']").each(function() {
    var str = $(this).attr('class');
    var classNames = str.split(" ");
    for (var i = 0; i < classNames.length; i++) {
      if(classNames[i].substring(0, 6) == 'no-js-') {
        $('.' + classNames[i]).removeClass(classNames[i].substring(6));
        }
      }
    });
  });

There are two sections to this. The first half deals with .only-js-x and the second half deals with .no-js-x.

$("div[class^='only-js-'],div[class*=' only-js-']").each(function() {

This applies the following function to each element that matches this selector, one at a time. The selector matches any element whose list of classes starts with .only-js- or whose list of classes contains .only-js-. The space ensures that it only matches elements with classes which start with .only-js-.

var str = $(this).attr('class');
  var classNames = str.split(" ");
  for (var i = 0; i < classNames.length; i++) {

The first line gets the string containing the class names. The second line splits it into an array, one class name per element of the array. The third line loops through each class name in the array.

if(classNames[i].substring(0, 8) == 'only-js-') {
  $('.' + classNames[i]).addClass(classNames[i].substring(8));
  }

If a class name starts with .only-js-, then class .x is added to the currently selected element, as in .only-js-x. The substring() call inside the addClass() call retrieves the second half of the class name, the half containing the class name to add.

The second half of JQuery is identical to the first, except it looks for .no-js-x. When it has a match, it removes class .x from the element.

Uses

I am using Foundation for my current web project, and these utility classes will be useful for Foundation's navigation, which has little fallback support for many features when Javascript is disabled. Some Foundation menu items have the class .has-dropdown, which adds a little drop down arrow as well as allowing the Javascript capability. However, hovering over a menu item with a class .has-dropdown and nothing happening can be very confusing to a user without JS. So instead, I will add the class .only-js-has-dropdown to the menu item, which will ensure the extra styling is applied only when it is functional.