
The Bouncer.js JavaScript library is created to extend the native HTML5 form validation that enables the customizable form validation on the form fields.
Features:
- Validate form fields on submit, blur and data change.
- Custom validation rules.
- Easy to add your own validators.
- Custom error messages.
- Useful API methods and events.
See also:
How to use it:
Download and insert the JavaScript file bouncer.polyfills.js into the web page.
<script src="dist/bouncer.polyfills.js"></script>
Initialize the Bouncer library and we’re ready to go.
var bouncer = new Bouncer('[data-validate]')Apply validation rules to the form fields as follows:
<form class="validate-me" id="validate-me" data-validate>
<div>
<label for="file">Upload any file</label>
<input name="file" id="file" type="file" data-bouncer-target="#file-error-msg" required>
</div>
<div id="file-error-msg"></div>
<div>
<label for="color">Color Picker <span class="pattern color">7-Character Hexadecimal (ex. #f7f7f7)</span></label>
<input type="color" name="color" id="color" pattern="#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})" required>
</div>
<div>
<label for="date">Date <span class="pattern date">YYYY-MM-DD</span></label>
<input type="date" name="date" id="date" required>
</div>
<div>
<label for="time">Time <span class="pattern time">HH:MM (24-hour time)</span></label>
<input type="time" name="time" id="time" required>
</div>
<div>
<label for="month">Month <span class="pattern month">YYYY-MM</span></label>
<input type="month" name="month" id="month" required>
</div>
<div>
<!-- Regex: https://gist.github.com/badsyntax/719800 -->
<label for="email">Email</label>
<input type="email" name="email" id="email" data-bouncer-message="The domain portion of the email address is invalid (the portion after the @)." required>
</div>
<div>
<!-- Regex: https://gist.github.com/dperini/729294 -->
<label for="url">URL</label>
<input type="url" name="url" id="url" data-bouncer-message="The URL is a missing a TLD (for example, .com)." required>
</div>
<div>
<!-- Regex: https://gist.github.com/dperini/729294 -->
<label for="urlnotld">URL without TLD allowed</label>
<input type="url" name="urlnotld" id="urlnotld" pattern="(?:(?:https?|HTTPS?|ftp|FTP):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-zA-Z\u00a1-\uffff0-9]-*)*[a-zA-Z\u00a1-\uffff0-9]+)(?:\.(?:[a-zA-Z\u00a1-\uffff0-9]-*)*[a-zA-Z\u00a1-\uffff0-9]+)*)(?::\d{2,5})?(?:[\/?#]\S*)?" required>
</div>
<div>
<label for="number">Number</label>
<input type="number" name="number" id="number" required>
</div>
<div>
<label for="float">Number (no decimals)</label>
<input type="number" step="any" name="integer" id="integer" pattern="^(?:[-+]?[0-9]*)$" required>
</div>
<div>
<label for="numberminmax">Number with Min and Max <span class="pattern">Must be between 2 and 7</span></label>
<input type="number" min="2" max="7" name="numberminmax" id="numberminmax" required>
</div>
<div>
<label for="tel">Tel <span class="pattern">123-456-7890</span></label>
<input type="text" name="tel" id="tel" pattern="^(?:\d{3}[\-]\d{3}[\-]\d{4})$" required>
</div>
<div>
<label for="password">Password <span class="pattern">At least 1 uppercase character, 1 lowercase character, and 1 number</span></label>
<input type="password" name="password" id="password" data-bouncer-message="Please choose a password that includes at least 1 uppercase character, 1 lowercase character, and 1 number." pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).*" required>
</div>
<div>
<label for="confirm-password">Confirm Password <span class="pattern">must match the field above</span></label>
<input type="password" name="confirm-password" id="confirm-password" data-bouncer-match="#password" data-bouncer-mismatch-message="Your passwords do not match." required>
</div>
<div>
<label for="textminmax">Text with MinLength and MaxLength <span class="pattern">must be between 3 and 9 characters long</span></label>
<input type="text" minlength="3" maxlength="9" name="textminmax" id="textminmax" required>
</div>
<div>
<label for="select">Select</label>
<select name="select" id="select" required>
<option></option>
<option>Harry Potter</option>
<option>Lord of the Rings</option>
<option>Star Wars</option>
<option>Start Trek</option>
</select>
</div>
<div>
<strong>Radio Buttons</strong>
<label class="label-normal">
<input type="radio" name="radio" id="radio-1" required>
Yes
</label>
<label class="label-normal">
<input type="radio" name="radio" id="radio-2" required>
No
</label>
</div>
<div>
<strong>Checkboxes</strong>
<label class="label-normal">
<input type="checkbox" name="checkbox-1" id="checkbox-1" required>
Wolverine (must be checked)
</label>
<label class="label-normal">
<input type="checkbox" name="checkbox-2" id="checkbox-2">
Storm
</label>
<label class="label-normal">
<input type="checkbox" name="checkbox-3" id="checkbox-3">
Cyclops
</label>
<label class="label-normal">
<input type="checkbox" name="checkbox-4" id="checkbox-4">
Gambit
</label>
</div>
<input type="submit" class="button" value="Submit">
</form>Create a custom validator.
var bouncer = new Bouncer('[data-validate]',{
customValidations: {
valueMismatch: function (field) {
// Look for a selector for a field to compare
// If there isn't one, return false (no error)
var selector = field.getAttribute('data-bouncer-match');
if (!selector) return false;
// Get the field to compare
var otherField = field.form.querySelector(selector);
if (!otherField) return false;
// Compare the two field values
// We use a negative comparison here because if they do match, the field validates
// We want to return true for failures, which can be confusing
return otherField.value !== field.value;
}
},
messages: {
valueMismatch: function (field) {
var customMessage = field.getAttribute('data-bouncer-mismatch-message');
return customMessage ? customMessage : 'Please make sure the fields match.'
}
}
})Customize the error messages.
var bouncer = new Bouncer('[data-validate]',{
messageAfterField: true,
messageCustom: 'data-bouncer-message',
messageTarget: 'data-bouncer-target',
messages: {
missingValue: {
checkbox: 'This field is required.',
radio: 'Please select a value.',
select: 'Please select a value.',
'select-multiple': 'Please select at least one value.',
default: 'Please fill out this field.'
},
patternMismatch: {
email: 'Please enter a valid email address.',
url: 'Please enter a URL.',
number: 'Please enter a number',
color: 'Please match the following format: #rrggbb',
date: 'Please use the YYYY-MM-DD format',
time: 'Please use the 24-hour time format. Ex. 23:00',
month: 'Please use the YYYY-MM format',
default: 'Please match the requested format.'
},
outOfRange: {
over: 'Please select a value that is no more than {max}.',
under: 'Please select a value that is no less than {min}.'
},
wrongLength: {
over: 'Please shorten this text to no more than {maxLength} characters. You are currently using {length} characters.',
under: 'Please lengthen this text to {minLength} characters or more. You are currently using {length} characters.'
},
fallback: 'There was an error with this field.'
},
})More configuration options.
var bouncer = new Bouncer('[data-validate]',{
// Classes & IDs
fieldClass: 'error',
errorClass: 'error-message',
fieldPrefix: 'bouncer-field_',
errorPrefix: 'bouncer-error_',
// Patterns
patterns: {
email: /^([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22))*\x40([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d))*(\.\w{2,})+$/,
url: /^(?:(?:https?|HTTPS?|ftp|FTP):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-zA-Z\u00a1-\uffff0-9]-*)*[a-zA-Z\u00a1-\uffff0-9]+)(?:\.(?:[a-zA-Z\u00a1-\uffff0-9]-*)*[a-zA-Z\u00a1-\uffff0-9]+)*(?:\.(?:[a-zA-Z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/,
number: /^(?:[-+]?[0-9]*[.,]?[0-9]+)$/,
color: /^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/,
date: /(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))/,
time: /^(?:(0[0-9]|1[0-9]|2[0-3])(:[0-5][0-9]))$/,
month: /^(?:(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])))$/
},
// Custom Validations
customValidations: {},
// Form Submission
disableSubmit: false,
// Custom Events
emitEvents: true
})API methods.
// validate a specific form field bouncer.validate(field); // validate all form fields bouncer.validateAll(); // destroy the library bouncer.destroy();
Event handlers.
document.addEventListener('bouncerShowError', function (event) {
// when an error is displayed
});
document.addEventListener('bouncerRemoveError', function (event) {
// when an error is removed
});
document.addEventListener('bouncerFormValid', function (event) {
// when the form is valid
});
document.addEventListener('bouncerFormInvalid', function (event) {
// when the form is invalid
}, false);
document.addEventListener('bouncerInitialized', function (event) {
// when the library is initialized
});
document.addEventListener('bouncerDestroy', function (event) {
// when the library is destroyed
});






