Fallbacks for disabled JavaScript…

on , most recently modified on

JavaScript is my favourite programming language of all. Yes, you read that right. It is a programming language and I think one is a fool if they believe otherwise. While it is the de facto client side scripting language of the web, JavaScript has also seen a lot of growth on the server side especially with the emergence of technologies like Node.js and applications built on it.

With such extensive use, you might think no person on the face of this earth would have it disabled in their browsers. Even if they did, having a severely limited Facebook should push them to live with whatever grievances they might have had. It appears, however, that a little over 1% of users disable their JavaScript. Some do it to prevent ads, some to avoid interference with their screen-readers and, of course, there are those who do web development with progressive enhancement in mind.

Instead of using the <noscript> element hold a message that attempts to urge people into enabling their JavaScript, it would be nicer if you could make them available to the user anyway. What I’ll be showing you here in this piece is a few ways to do this thing I’d like to call regressive enhancement. I call it that to differentiate designing based on available yet disabled features from designing for browsers that do not support the newer features – it’s a fine line.

Detecting disabled JavaScript

Since scripts do not work when disabled we will have to use a combination of ideas that work without them. The first thing to remember is that we do not want to penalise users who use JavaScript. We’ll have to write something that targets the non JS users specifically.

Redirection

Let’s say we are to redirect to the same page except we’ll be adding a query-string with the field “nojs=true”. For a page like http://denverdias.com/ that could be done with a header like so.

Refresh: 0; url=http://denverdias.com/?nojs=true

In this, 0 is the number of seconds it waits before refreshing. It can be any number. The same effective command can be conveyed via a <meta> element.

<meta http-equiv="refresh" content="0; url=http://denverdias.com/?nojs=true" />

Of course, you want redirects to only happen on JS disabled devices. So you can wrap that in a <noscript> element.

<noscript><meta http-equiv="refresh" content="0; url=http://denverdias.com/?nojs=true" /></noscript>

That can also be written like so. (The ‘url=’ has been removed.)

<noscript><meta http-equiv="refresh" content="0;http://denverdias.com/?nojs=true" /></noscript>

Handling the no JS

On the server side, the query string could be parsed to set a global constant. Depending on the boolean value stored in the constant, the server could make decisions as to whether or not scripts should be delivered.

In PHP you could do this.

define( 'HAS_JS', ( isset( $_GET['nojs'] ) && $_GET['nojs'] === 'true' ) );

That way you could only include scripts if the constant HAS_JS has the value of true. The issue you have to deal with now though is that every request that comes from a user with JS disabled will trigger a second request with the “nojs=true” field in the query-string. We can fix that by setting a cookie on the client’s system that can, on subsequent requests, be used to set the our HAS_JS constant.

In PHP that would be…

function set_JS_Cookie() {
  if ( HAS_JS ) {
    setcookie( 'nojs', null, -1, COOKIEPATH, COOKIE_DOMAIN, false );
  }
  else {
    setcookie( 'nojs', 'true', time() + 3600 * 24 * 30, COOKIEPATH, COOKIE_DOMAIN, false );
  }
}

Run this function any time before the HTML in your PHP pages is parsed. In WordPress, that function can be hooked to it’s ‘init’ event.

add_action( 'init', 'set_JS_Cookie' );

You will have to change the definition of HAS_JS to the following.

define( 'HAS_JS', !( ( !isset($_GET['nojs']) && ( isset( $_COOKIE['nojs'] ) && $_COOKIE['nojs'] === 'true' ) ) || $_GET['nojs'] === 'true' ) );

I’ll leave it to you to decipher that snippet.

Of course, if the user enables JavaScript at any point in time, you’d like to know about it right away so you could serve your normal pages once again. Inject the following script into your JS disabled markup.

<script>location.search += ( location.search === '' ? '?' : '&' ) + 'nojs=false'</script>

This ensures that when the script is encountered in a JS enabled browser it immediately makes a second request with a false nojs field. That will effectively delete your earlier cookie and return everything back to normal.

Using this solution, you’ll never need to penalise any user too much except when they fiddle with their browser settings.

How to use it

Well, if you’re using WordPress, you will have a function that hooks to ‘wp_enqueue_scripts’ in your theme’s functions.php. You can selectively include scripts that are enqueued within that function. Script manager classes exist in every web technology now. I’m sure you’ll figure something out.

Another trick you could use is leverage CSS’s next sibling selector. It works like the following.

p + p {
  text-indent: 3em;
}

This will ensure every paragraph element that immediately follows a paragraph element will have it’s first line indented by thrice the font size. What I’m proposing though is using the following snippet.

input:checked + pre {
  border-color: #000;
  background-color: #000;
  color: #FFF;
}

In this code block right above what you’re currently reading are styles that will be applied to any <pre> element that follows an input box which has the pseudo class :checked. In fact, there is a hidden checkbox placed just before that pre element. Yes, this article’s pretty meta. You can trigger checking / unchecking of that checkbox by clicking the following button. Go ahead! Give it a try…

There is no JavaScript running in the background. You can have a view at the source of this page if you want. That button above looks like one but isn’t technically a <button> element. It is a <label> element and in HTML, Labels have a special ability. By setting it’s for attribute to the ID of the <input> element it targets, it can check / uncheck it if it’s a checkbox or a radio-button. If it targets a text box, it will focus on it and place a cursor for you there. This will work even with JavaScript disabled. You can give that a try too if you like. Literally everything on this page that uses JavaScript will fail except for that button.

Now, that was a simple example. You are only limited by your imagination. You can use CSS transforms and transitions and they’ll all work brilliantly. Sure, you’ll be basically generating bad markup but it’s only going to appear on 1% of your viewership so I think that’s a pretty sweet bargain considering you can make your site work even when crippled.

And that’s it!

Let me know if you’ve found this piece helpful. Remember to turn your JavaScript back on before clicking on all of those share buttons because I didn’t take my own advice to set up these fallbacks. Have you applied these tricks or seen them applied elsewhere? Let me know in the comments along with any queries you have about anything JS related. I’ll be watching out.

Until the next time…