custom stylable validation bubbles with polyfilled HTML5, CSS3 and ES5 (polyfilled cross-browser + using html5 extensions)

Removes the native error tooltip of HTML5 capable browsers and replaces it with a styleable validation hint. It's a fork of @http://jsfiddle.net/trixta/L4NDp/

by trixta

HTML

<script src="http://afarkas.github.com/webshim/demos/js-webshim/minified/extras/modernizr-custom.js"></script>
<script src="http://afarkas.github.com/webshim/demos/js-webshim/minified/extras/loaders/sssl.js"></script>
<script src="http://afarkas.github.com/webshim/demos/js-webshim/minified/polyfiller.js"></script>
<form class="example">
    <div>
        <label for="your-name">Name: *</label>
        <input type="text" required="required" id="your-name" name="yourName" />
    </div>
    <div>
        <label for="email">email: *</label>
        <input type="email" required="required" id="email" name="email" />
    </div>
    
    <div>
        <label for="name-custom">custom error-message: *</label>
        <input required="required" type="text" id="name-custom" name="name-custom" data-errormessage="Please enter something" title="" />
    </div>
    <div>
        <label for="multiple-name-custom">custom error-message: *</label>
        <input required="required" type="email" id="multiple-name-custom" name="multiple-name-custom" data-errormessage='{"valueMissing": "Please enter an email address", "typeMismatch": "This is not a valid email address. Please enter a valid one"}' title="" />
    </div>
    <div>
        <input type="submit" />
    </div>
</form>

<p><a href="http://afarkas.github.com/webshim/demos/index.html" target="_blank">uses webhsims lib</a></p>
<p><a href="http://jsfiddle.net/trixta/XqPhQ/" target="_top">instant form validation example</a></p>

CSS

body {
    font: 100% arial,helvetica,sans-serif;
}
.validity-alert span.va-box {
    box-shadow: 2px 2px 5px #E8673B;
    -moz-box-shadow: 2px 2px 5px #E8673B;
    background: rgb(255, 255, 255);
    background: rgba(255, 255, 255, 0.9);
    color: #000;
}
.show-error-bubble {
    opacity: 1;
    visibility: visible;
}
.example {
    margin: 10px;
    padding: 1px 8px;
    background: #fff6c6;
    border-radius: 10px;
    width: 420px;
}
.example > div:after {
    content: " ";
    display: block;
    clear: both;
}
/* animate all */
.example > div,
.example input[type=text],
.example input[type=email] {
    -moz-transition-property: all;
    -moz-transition-duration: 1s;
    -webkit-transition-property: all;
    -webkit-transition-duration: 1s;
    -o-transition-property: all;
    -o-transition-duration: 1s;
    -ms-transition-property: all;
    -ms-transition-duration: 1s;
    transition-property: all;
    transition-duration: 1s;
}
.example > div {
    margin: 15px 10px;
    padding: 5px 10px;
    border-radius: 10px;
    background: #ffe268;
    box-shadow: 3px 3px 3px #e0c75b;
}
.example > div.invalid {
    background: #e8673b;
    box-shadow: 3px 3px 3px #e8673b;
}
.example > div.valid {
    background: #b0ea74;
    box-shadow: 3px 3px 3px #a3d96c;
}
.example input {
    box-shadow: none;
}
.example input[type=text],
.example input[type=email] {
    float: right;
    display: inline-bock;
    padding: 0.3em;
    height: 1.1em;
    width: 180px;
    border: 1px solid #d8bf58;
    border-radius: 4px;
    box-shadow: inset 1px 1px 1px #e0c75b;
    background: #fff;
}
.example > div.valid input {
    border: 1px solid #9bce67;
    box-shadow: inset 1px 1px 1px #87b45a;
}
.example > div.invalid input {
    border: 1px solid #a81107;
    box-shadow: inset 1px 1px 1px #890e06;
}
.example label {
    margin: 0.1em 0 0;
    cursor: pointer;
    display: inline-block;
    vertical-align: middle;
}
.example input[type=submit] {
    width: 100%;
    text-align: center;
}

JavaScript

(function() {
    "use strict";
    //load ES5 and HTML5 form features, if not implemented
    $.webshims.polyfill('es5 forms');

    $(function() {
        //objectCreate behaves similiar to Object.create
        window.validationUI = $.webshims.objectCreate({
            init: function() {
                $(document)
                    .bind('focusout', this.testValidity.bind(this))
                    //invalid is a simple, non-bubbling event: 
                    //webshims lib sets the useCapture argument in addEventListener to true, 
                    //so that the event is also fired on parent elements
                    .bind('invalid', this.handleInvalid.bind(this))
                    //firstinvalid-event is a webshims extension and is triggered only on the first invalid element
                    .bind('firstinvalid', function(e) {
                        //$.webshims.validityAlert is an helper object to create simple, styleable error messages
                        $.webshims.validityAlert.showFor(e.target);
                    }
                );
            },
            switchClasses: function(elem, removeClass, addClass) {
                $(elem).parent().addClass(addClass).removeClass(removeClass);
            }
        }, {
            testValidity: {
                value: function(e) {
                    if (!$(e.target).prop('willValidate') || $(e.target).prop('type') == 'submit') {
                        return;
                    }
                    if ($(e.target).is(':valid')) {
                        this.switchClasses(e.target, "invalid", "valid");
                    } else {
                        this.switchClasses(e.target, "valid", "invalid");
                    }
                }
            },
            handleInvalid: {
                value: function(e) {
                    this.switchClasses(e.target, "valid", "invalid");
                    //preventDefault to remove the native browser error UI
            ...