This site contains the document for old version. Please upgrade to the latest version v1.0.0

Supporting Spectre framework

Examples

Spectre is a lightweight, responsive and modern CSS framework for faster and extensible development. You can see more information and its usage on the official website.

This example is a step by step instruction showing how to validate the forms which uses the Spectre framework.

Downloading the files

  • Create a file named spectre.js with the following content:
/**
 * This class supports validating Spectre framework (https://picturepan2.github.io/spectre/)
 */
(function($) {
    FormValidation.Framework.Spectre = function(element, options) {
        options = $.extend(true, {
            button: {
                selector: '[type="submit"]:not([formnovalidate])',
                // The class of disabled button
                disabled: 'fv-button-disabled'
            },
            err: {
                clazz: 'fv-help-block',
                parent: '^(.*)col-(xs|sm|md)-[0-9]+(.*)$'
            },
            // Spectre doesn't support feedback icon
            icon: {
                valid: null,
                invalid: null,
                validating: null,
                feedback: 'fv-control-feedback'
            },
            row: {
                selector: '.form-group',
                valid: 'has-success',
                invalid: 'has-danger',
                feedback: 'fv-has-feedback'
            }
        }, options);

        FormValidation.Base.apply(this, [element, options]);
    };

    FormValidation.Framework.Spectre.prototype = $.extend({}, FormValidation.Base.prototype, {
        /**
         * Specific framework might need to adjust the icon position
         *
         * @param {jQuery} $field The field element
         * @param {jQuery} $icon The icon element
         */
        _fixIcon: function($field, $icon) {
            var ns      = this._namespace,
                type    = $field.attr('type'),
                field   = $field.attr('data-' + ns + '-field'),
                $row    = $field.closest(this.options.fields[field].row || this.options.row.selector),
                $parent = $field.parent();

            // Place it after the container of checkbox/radio
            // so when clicking the icon, it doesn't effect to the checkbox/radio element
            if ('checkbox' === type || 'radio' === type) {
                if ($parent.is('label')) {
                    $parent.addClass('fv-' + type);
                    $icon.insertAfter($parent);
                }
            }
        }
    });
}(jQuery));
  • Create a file named spectre.css with the following content:
.fv-form-spectre .fv-control-feedback {
    width: 32px;    /* Height of Spectre input */
    height: 32px;
    line-height: 32px;
}

.fv-form-spectre .fv-help-block {
    margin-top: 5px;
}

.fv-form-spectre .fv-control-feedback {
    top: 21px;      /* Same as height of label */
}

/* Horizontal form */
.fv-form-spectre.form-horizontal .fv-control-feedback {
    top: 0;
}

.fv-form-spectre:not(.form-horizontal) label.sr-only~.fv-control-feedback {
    top: -4px;      /* labelHeight/2 - iconHeight/2 */
}

.fv-form-spectre .has-danger label,
.fv-form-spectre .has-danger .fv-help-block,
.fv-form-spectre .has-danger .fv-control-feedback {
    color: #e85600; /* Same as .has-danger */
}

.fv-form-spectre .has-success label,
.fv-form-spectre .has-success .fv-help-block,
.fv-form-spectre .has-success .fv-control-feedback {
    color: #32b643; /* Same as .has-success */
}

You should place spectre.js and spectre.css in formvalidation/dist/js/framework and formvalidation/dist/css directories respectively.

Including the library

<!-- Skeleton framework -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/spectre.css/0.1.25/spectre.min.css" rel="stylesheet">

<!-- Optional: Include the Font Awesome for using some icons -->
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet" />

<!-- FormValidation CSS file -->
<link rel="stylesheet" href="/vendor/formvalidation/dist/css/formValidation.min.css">
<link rel="stylesheet" href="/vendor/formvalidation/dist/css/spectre.css">

<!-- jQuery v1.9.1 or higher -->
<script type="text/javascript" src="/vendor/jquery/jquery.min.js"></script>

<!-- FormValidation plugin and the class supports validating Skeleton form -->
<script src="/vendor/formvalidation/dist/js/formValidation.min.js"></script>
<script src="/vendor/formvalidation/dist/js/framework/spectre.js"></script>

Calling the plugin

When calling .formValidation(), you need to set the framework option:

$(form).formValidation({
    framework: 'spectre',
    ...
});
Some of forms use the row option to support multiple fields in the same row container

Stacked form

<form id="signupForm" method="post">
    <div class="form-group">
        <label class="form-label">Full name</label>
        <input name="fullName" type="text" class="form-input" />
    </div>

    <div class="form-group">
        <label class="form-label">Username</label>
        <input name="username" type="text" class="form-input" />
    </div>

    <div class="form-group">
        <label class="form-label">Email address</label>
        <input name="email" type="text" class="form-input" />
    </div>

    <div class="form-group">
        <label class="form-label">Password</label>
        <input name="password" type="password" class="form-input" />
    </div>

    <div class="form-group">
        <label class="form-label">Gender</label>
        <label class="form-radio">
            <input name="gender" type="radio" value="male" />
            <i class="form-icon"></i> Male
        </label>
        <label class="form-radio">
            <input name="gender" type="radio" value="female" />
            <i class="form-icon"></i> Female
        </label>
        <label class="form-radio">
            <input name="gender" type="radio" value="other" />
            <i class="form-icon"></i> Other
        </label>
    </div>

    <div class="form-group">
        <label class="form-label"><span id="captchaOperation"></span></label>
        <input name="captcha" type="password" class="form-input" />
    </div>

    <div class="form-group">
        <label class="sr-only"></label>
        <label class="form-checkbox">
            <input name="agree" type="checkbox" />
            <i class="form-icon"></i> Agree with the terms and conditions
        </label>
    </div>

    <div class="form-group">
        <button type="submit" class="btn btn-primary">Sign up</button>
    </div>
</form>

<script>
$(document).ready(function() {
    // Generate a simple captcha
    function randomNumber(min, max) {
        return Math.floor(Math.random() * (max - min + 1) + min);
    };
    $('#captchaOperation').html([randomNumber(1, 100), '+', randomNumber(1, 200), '='].join(' '));

    $('#signupForm').formValidation({
        framework: 'spectre',
        icon: {
            valid: 'fa fa-check',
            invalid: 'fa fa-times',
            validating: 'fa fa-refresh',
            feedback: 'fv-control-feedback'
        },
        fields: {
            fullName: {
                validators: {
                    notEmpty: {
                        message: 'The full name is required'
                    }
                }
            },
            username: {
                validators: {
                    notEmpty: {
                        message: 'The username is required'
                    },
                    stringLength: {
                        min: 6,
                        max: 30,
                        message: 'The username must be more than 6 and less than 30 characters long'
                    },
                    regexp: {
                        regexp: /^[a-zA-Z0-9_\.]+$/,
                        message: 'The username can only consist of alphabetical, number, dot and underscore'
                    }
                }
            },
            email: {
                validators: {
                    notEmpty: {
                        message: 'The email address is required'
                    },
                    emailAddress: {
                        message: 'The input is not a valid email address'
                    }
                }
            },
            password: {
                validators: {
                    notEmpty: {
                        message: 'The password is required'
                    },
                    different: {
                        field: 'username',
                        message: 'The password cannot be the same as username'
                    }
                }
            },
            gender: {
                validators: {
                    notEmpty: {
                        message: 'The gender is required'
                    }
                }
            },
            captcha: {
                validators: {
                    callback: {
                        message: 'Wrong answer',
                        callback: function(value, validator) {
                            var items = $('#captchaOperation').html().split(' '), sum = parseInt(items[0]) + parseInt(items[2]);
                            return value == sum;
                        }
                    }
                }
            },
            agree: {
                validators: {
                    notEmpty: {
                        message: 'You must agree with the terms and conditions'
                    }
                }
            }
        }
    });
});
</script>

Horizontal form

<form id="signupForm" class="form-horizontal" method="post">
    <div class="form-group">
        <label class="form-label col-sm-3">Full name</label>

        <div class="col-sm-6">
            <input name="fullName" type="text" class="form-input" placeholder="Full name" />
        </div>
    </div>

    <div class="form-group">
        <label class="form-label col-sm-3">Username</label>

        <div class="col-sm-6">
            <input name="username" type="text" class="form-input" />
        </div>
    </div>

    <div class="form-group">
        <label class="form-label col-sm-3">Email address</label>

        <div class="col-sm-6">
            <input name="email" type="text" class="form-input" />
        </div>
    </div>

    <div class="form-group">
        <label class="form-label col-sm-3">Password</label>

        <div class="col-sm-6">
            <input name="password" type="password" class="form-input" />
        </div>
    </div>

    <div class="form-group">
        <label class="form-label col-sm-3">Gender</label>

        <div class="col-sm-6">
            <label class="form-radio">
                <input name="gender" type="radio" value="male" />
                <i class="form-icon"></i> Male
            </label>
            <label class="form-radio">
                <input name="gender" type="radio" value="female" />
                <i class="form-icon"></i> Female
            </label>
            <label class="form-radio">
                <input name="gender" type="radio" value="other" />
                <i class="form-icon"></i> Other
            </label>
        </div>
    </div>

    <div class="form-group">
        <label class="form-label col-sm-3">
            <span id="captchaOperation"></span>
        </label>

        <div class="col-sm-3">
            <input type="text" name="captcha" class="form-input" />
        </div>
    </div>

    <div class="form-group">
        <label class="form-label col-sm-3"></label>
        <div class="col-sm-9">
            <label class="form-checkbox">
                <input name="agree" type="checkbox" />
                <i class="form-icon"></i> Agree with the terms and conditions
            </label>
        </div>
    </div>

    <div class="form-group">
        <label class="form-label col-sm-3"></label>
        <div class="col-sm-9">
            <button type="submit" class="btn btn-primary">Sign up</button>
        </div>
    </div>
</form>

<script>
$(document).ready(function() {
    // Generate a simple captcha
    function randomNumber(min, max) {
        return Math.floor(Math.random() * (max - min + 1) + min);
    };
    $('#captchaOperation').html([randomNumber(1, 100), '+', randomNumber(1, 200), '='].join(' '));

    $('#signupForm').formValidation({
        framework: 'spectre',
        icon: {
            valid: 'fa fa-check',
            invalid: 'fa fa-times',
            validating: 'fa fa-refresh',
            feedback: 'fv-control-feedback'
        },
        fields: {
            fullName: {
                validators: {
                    notEmpty: {
                        message: 'The full name is required'
                    }
                }
            },
            username: {
                validators: {
                    notEmpty: {
                        message: 'The username is required'
                    },
                    stringLength: {
                        min: 6,
                        max: 30,
                        message: 'The username must be more than 6 and less than 30 characters long'
                    },
                    regexp: {
                        regexp: /^[a-zA-Z0-9_\.]+$/,
                        message: 'The username can only consist of alphabetical, number, dot and underscore'
                    }
                }
            },
            email: {
                validators: {
                    notEmpty: {
                        message: 'The email address is required'
                    },
                    emailAddress: {
                        message: 'The input is not a valid email address'
                    }
                }
            },
            password: {
                validators: {
                    notEmpty: {
                        message: 'The password is required'
                    },
                    different: {
                        field: 'username',
                        message: 'The password cannot be the same as username'
                    }
                }
            },
            gender: {
                validators: {
                    notEmpty: {
                        message: 'The gender is required'
                    }
                }
            },
            captcha: {
                validators: {
                    callback: {
                        message: 'Wrong answer',
                        callback: function(value, validator) {
                            var items = $('#captchaOperation').html().split(' '), sum = parseInt(items[0]) + parseInt(items[2]);
                            return value == sum;
                        }
                    }
                }
            },
            agree: {
                validators: {
                    notEmpty: {
                        message: 'You must agree with the terms and conditions'
                    }
                }
            }
        }
    });
});
</script>