In a discussion on Scratch Forums on the merits of CAPTCHA, one of our members suggested a possible alternative that detected JavaScript events (mouse events, keystrokes) to show that it's a human completing the form.

I took on the challenge of creating a simple JS-based alternative to CAPTCHA, which I'll explain below.

High-level Requirements

  1. The form should record certain events to prove human involvement.
  2. If JavaScript is not enabled, there needs to be a fallback solution.
  3. The tests should be invisible to the user where possible.
  4. It should not be possible for a bot to discover the test and to spoof the results.

Am I Human?

First of all, our form will need some way of sending the answer to "am I human" through to the back-end script.

For this, I'll just use a hidden field. Note, following requirement 4 above, this field could be named anything. If bots learn the likely name for this field, they could send the correct value through and bypass the system.

I'll put the hidden field before the closing form tag (but you could put it anywhere in the form).

 <input type="hidden" name="imahuman" id="imahuman" value="0" />
</form>

The value of this field is zero by default, which means "no - not a human".

Switch it For Humans

We want to set the value of "imahuman" (or your own name, please) when certain events take place, which we'll detect using Javascript.

Following best practice, the function that sets up the event detection should be called by an event handler, and that event handler should be attached using an external Javascript file, not written into your HTML code. Like this…

// Set up
addEvent(window, "load", setUpHumanTest, false);
function setUpHumanTest() {
  var myforms = document.getElementsByTagName("form") ;
  for (var i=0; i<myforms.length; i++) {
    addEvent(myforms[i], "focus", markAsHuman, false);
    addEvent(myforms[i], "click", markAsHuman, false);
  }
}

// Identify a human
function markAsHuman() {
  document.getElementById("imahuman").value = "1";
}

// Generic cross-browser code for attaching events to elements
// You should really have this in a separate common JS file
var addEvent;
if (document.addEventListener) {
  addEvent = function(element, type, handler) {
  element.addEventListener(type, handler, null);
  if (element.href) element.href="javascript:void('');" ;
  }
}
else if (document.attachEvent) {
  addEvent = function(element, type, handler) {
    element.attachEvent("on" + type, handler);
    if (element.href) element.href="javascript:void('');" ;
  }
}
else {
  addEvent = new Function; // not supported
}

What's happening here (working from bottom upwards):

Testing Results with Script

Your form will submit to a middleware script (PHP, ASP etc.) You'll simply need to tell this script to reject any submissions that still have "imahuman" (or equivalent) set to zero (or whatever you use).

Personally, I like to pretend that the script has worked, and forward the bot to the success page, but not do whatever is supposed to happen with a valid input. I figure that there's no reason to tell a bot that you've twigged its game and draw attention to your site.

What About When JavaScript is Off?

Clearly, we need to handle the (approximately 10%) minority that don't use JS. This now includes some mobile browsers and text-to-speech readers, not just people exercising their choice for a diminshed browsing experience.

It's very easy to show extra special HTML when JS is not working: you just use the <noscript></noscript> tag.

Here's what I do:

 <noscript>
  <dt>What is three plus four?</dt>
  <dd><input type="text" maxlength="1" name="althuman" /></dd>
</noscript>

This is really simple, but you could replace it with a CAPTCHA test if you prefer.

(Note, we usually use definition lists for form layout these days. Not the strictest semantic application of the tag type, but I think it's OK, in the broader application of name/value pairs.)

The field only allows a one-character response, so you have to type "7". This is also easy to change.

The back-end script would then need to test first whether "imahuman" is set "on", OR if the alternative human check is correct. If either is OK, we're OK.

I hope you find this technique easy to implement and useful on your own sites. Please post any improvements to the forum thread.

Do you love our approach to crafting simple & effective web sites that just work for people?

We'd love to hear about your web strategy. Contact one of our team today!

Search this site
Pro Tips
Learn how to make fantastic web site designs..
Buy "Save the Pixel" now!
On “Save the Pixel”
Clicss templates, great robust useful CSS templates from £40
Share this Article
Send to a friend now&hellip
Follow Ben Hunt on Twitter
Floor 3
111 Buckingham Palace Road
London
SW1W 0WQ
UK
Phone
+44 (0)207 1600 989