FormValidation v0.8.1 is released, supports Bootstrap 4 alpha 3

Using hint library to show message

Examples

Via the err.container option, FormValidation provides the ability of using tooltip to show the error messages.

What if you want to use custom tooltip library instead of built-in tooltip component provided by supported frameworks such as

This example is an answer of this question. By following step by step instructions, you will learn how to use an external tooltip library for displaying the messages.

This example uses the hint library for that purpose.

hint is one of popular libraries for creating tooltip with pure CSS. It's also available on Github.

Preparing the markup

Using hint is quite simple. We just need to include its CSS file firstly, and add some HTML attributes to the element:

<link rel="stylesheet" href="/path/to/hint.css">

<span class="hint--bottom hint--error" data-hint="Thank you!">hover over me.</span>

The CSS classes hint--bottom and hint--error indicate the tooltip position and kind of information. hint supports other positions and kinds via modifiers which are listed in its official website.

The problem is that it doesn't support self closing tag such as <img/>, <i/>. Therefore, it's not possible for you to attach a tooltip to an icon such as:

<i class="glyphicon glyphicon-remove hint--bottom hint--error"
   data-hint="The username is required"></i>

To fix that, we need to wrap the icon inside some span elements:

<span class="form-control-feedback">
    <span class="hint--bottom hint--error" data-hint="The username is required">
        <i class="glyphicon glyphicon-remove"></i>
    </span>
</span>

Hover the mouse over the icons to see it in action:

<link href="https://cdnjs.cloudflare.com/ajax/libs/hint.css/2.2.1/hint.min.css" rel="stylesheet" />

<style>
.form-control-feedback {
    pointer-events: auto;
}
</style>

<form id="registrationForm" class="form-horizontal">
    <div class="form-group">
        <label class="col-xs-3 control-label">Full name</label>

        <div class="col-xs-4 has-feedback has-error">
            <input type="text" class="form-control" name="firstName" placeholder="First name" />

            <span class="form-control-feedback">
                <span class="hint--bottom hint--error" data-hint="The first name is required">
                    <i class="glyphicon glyphicon-remove"></i>
                </span>
            </span>
        </div>

        <div class="col-xs-4 has-feedback has-error">
            <input type="text" class="form-control" name="lastName" placeholder="Last name" />

            <span class="form-control-feedback">
                <span class="hint--bottom hint--error" data-hint="The last name is required">
                    <i class="glyphicon glyphicon-remove"></i>
                </span>
            </span>
        </div>
    </div>

    <div class="form-group">
        <label class="col-xs-3 control-label">Username</label>
        <div class="col-xs-5 has-feedback has-error">
            <input type="text" class="form-control" name="username" />

            <span class="form-control-feedback">
                <span class="hint--bottom hint--error" data-hint="The username is required">
                    <i class="glyphicon glyphicon-remove"></i>
                </span>
            </span>
        </div>
    </div>
</form>

Because the icons (i) are generated dynamically by the plugin, we need to trigger the init.field.fv event to prepare the markup properly:

$('#registrationForm')
    .on('init.field.fv', function(e, data) {
        data.element            // Retrieve the field element
            .data('fv.icon')    // its icon element
            .wrap($('<span/>')) // Wrap inside a span ...
            .parent()
            // ... and another parent span
            .wrap($('<span/>').addClass('form-control-feedback'));
    })
    .formValidation(...);
The same technique is used in the Showing icons in custom area example

Showing the tooltip

In this step, we need to display the tooltip when hovering the mouse over the icon. It can be done by adding hint CSS and attribute to the target element (i.e, our created span element). This should happen whenever the field is invalid:

$('#registrationForm')
    .on('init.field.fv', function(e, data) {
        ...
    })
    .formValidation(...)
    .on('err.field.fv', function(e, data) {
        var $field    = data.element,
            $icon     = $field.data('fv.icon'),
            $messages = $field.data('fv.messages').find('.help-block[data-fv-for="' + data.field + '"]');

        // Get a random message
        var message = $messages.filter('[data-fv-result="INVALID"]').eq(0).html();

        $icon.removeClass()
             .addClass('glyphicon glyphicon-remove')
             .parent()
             .addClass('hint--bottom hint--error')
             .attr('data-hint', message);

        // Hide all error messages
        $messages.hide();
    });
The err.field.fv event is triggered when the field is invalid. For more usage examples, refer to the field events section.

As you can see above, the icon and all error messages elements can be retrieved by $field.data('fv.icon') and $field.data('fv.messages'), respectively.

Hiding the tooltip

It's more easy to hide the tooltip when the field is valid. We just remove the associated CSS class and attribute from the target element:

$('#registrationForm')
    .on('init.field.fv', function(e, data) {
        ...
    })
    .formValidation(...)
    .on('err.field.fv', function(e, data) {
        ...
    })
    .on('success.field.fv', function(e, data) {
        data.element
            .data('fv.icon')
            .removeClass()
            .addClass('glyphicon glyphicon-ok')
            .parent()
            .removeClass('hint--bottom hint--error')
            .removeAttr('data-hint');
    });
As opposite of the err.field.fv event, the success.field.fv event is triggered when the field is valid. For more usage examples, refer to the field events section.

Final version

Finally, you can get a full working demonstration as following:

<link href="https://cdnjs.cloudflare.com/ajax/libs/hint.css/2.2.1/hint.min.css" rel="stylesheet" />

<style>
.form-control-feedback {
    pointer-events: auto;
}
</style>

<form id="registrationForm" class="form-horizontal">
    <div class="form-group">
        <label class="col-xs-3 control-label">Full name</label>
        <div class="col-xs-4">
            <input type="text" class="form-control" name="firstName" placeholder="First name" />
        </div>
        <div class="col-xs-4">
            <input type="text" class="form-control" name="lastName" placeholder="Last name" />
        </div>
    </div>

    <div class="form-group">
        <label class="col-xs-3 control-label">Username</label>
        <div class="col-xs-5">
            <input type="text" class="form-control" name="username" />
        </div>
    </div>

    <div class="form-group">
        <label class="col-xs-3 control-label">Email address</label>
        <div class="col-xs-5">
            <input type="text" class="form-control" name="email" />
        </div>
    </div>

    <div class="form-group">
        <label class="col-xs-3 control-label">Password</label>
        <div class="col-xs-5">
            <input type="password" class="form-control" name="password" />
        </div>
    </div>

    <div class="form-group">
        <label class="col-xs-3 control-label">Gender</label>
        <div class="col-xs-5">
            <div class="radio">
                <label>
                    <input type="radio" name="gender" value="male" /> Male
                </label>
            </div>
            <div class="radio">
                <label>
                    <input type="radio" name="gender" value="female" /> Female
                </label>
            </div>
            <div class="radio">
                <label>
                    <input type="radio" name="gender" value="other" /> Other
                </label>
            </div>
        </div>
    </div>

    <div class="form-group">
        <label class="col-xs-3 control-label">Date of birth</label>
        <div class="col-xs-3">
            <input type="text" class="form-control" name="birthday" placeholder="YYYY/MM/DD" />
        </div>
    </div>

    <div class="form-group">
        <div class="col-xs-9 col-xs-offset-3">
            <button type="submit" class="btn btn-primary" name="signup" value="Sign up">Submit</button>
        </div>
    </div>
</form>

<script>
$(document).ready(function() {
    $('#registrationForm')
        .on('init.field.fv', function(e, data) {
            data.element
                .data('fv.icon')
                .wrap($('<span/>'))
                .parent()
                .wrap($('<span/>').addClass('form-control-feedback'));
        })
        .formValidation({
            framework: 'bootstrap',
            icon: {
                valid: 'glyphicon glyphicon-ok',
                invalid: 'glyphicon glyphicon-remove',
                validating: 'glyphicon glyphicon-refresh'
            },
            fields: {
                firstName: {
                    row: '.col-xs-4',
                    validators: {
                        notEmpty: {
                            message: 'The first name is required'
                        }
                    }
                },
                lastName: {
                    row: '.col-xs-4',
                    validators: {
                        notEmpty: {
                            message: 'The last 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'
                        }
                    }
                },
                birthday: {
                    validators: {
                        notEmpty: {
                            message: 'The date of birth is required'
                        },
                        date: {
                            format: 'YYYY/MM/DD',
                            message: 'The date of birth is not valid'
                        }
                    }
                }
            }
        })
        .on('err.field.fv', function(e, data) {
            var $field    = data.element,
                $icon     = $field.data('fv.icon'),
                $messages = $field.data('fv.messages').find('.help-block[data-fv-for="' + data.field + '"]');

            // Get a random message
            var message = $messages.filter('[data-fv-result="INVALID"]').eq(0).html();

            $icon.removeClass()
                 .addClass('glyphicon glyphicon-remove')
                 .parent()
                 .addClass('hint--bottom hint--error')
                 .attr('data-hint', message);

            // Hide all error messages
            $messages.hide();
        })
        .on('success.field.fv', function(e, data) {
            data.element
                .data('fv.icon')
                .removeClass()
                .addClass('glyphicon glyphicon-ok')
                .parent()
                .removeClass('hint--bottom hint--error')
                .removeAttr('data-hint');
        });
});
</script>

Related examples