Forms in Bootstrap are the biggest paradigm shift for us as ColdFusion developers. We all use cfform
and its cfinput
tags to perform standard form functionality and validation. Bootstrap forces us to use HTML5 instead of ColdFusion to perform these tasks. This is actually a good thing. Using the defined HTML5 methods gives us more control over how things are validated.
Another major shift in thinking is using tables to layout your forms. This has been the one exception to the "don't use tables" rule. The cool thing is Bootstrap allows us to use their grid system - or a version of it - to layout forms quickly and easily. We will cover that in "part 2" below.
Let's start easy here and look over the standard markup for form fields.
<label for="yourname">Your name</label>
<input class="form-control" name="yourname" id="yourname" type="text" placeholder="Enter your name">
Nothing too fancy here - just a plain ordinary text field. Let me draw your attention to three key items here.
label
tag is not strictly required but is highly recommended. Note that the value of the for
attribute is the same as the value of the field's id
attribute.id
attribute should be there as it is used for HTML5 validation and adds accessibility.class
is set to form-control
. That's really all you need to make your form elements use Bootstrap's visual style!col-xl-12
column it will be as wide as the whole layout. If you forget and just plop the field into a blank HTML page it will span the width of the viewport.
<label for="yourdate">Your date</label>
<input class="form-control" name="yourdate" id="yourdate" type="date" placeholder="">
<label for="youremail">Your e-mail address</label>
<input class="form-control" name="youremail" id="youremail" type="email" placeholder="Enter your e-mail address">
This one is pretty nifty. It automatically validates the input as an e-mail address.
<label for="yourpass">Your password</label>
<input class="form-control" name="yourpass" id="yourpass" type="password" placeholder="Enter your password">
<label for="yourssn">Your SSN</label>
<input class="form-control" name="yourssn" id="yourssn" type="text" placeholder="Enter your SSN" readonly>
<label for="yourcard">Your card number</label>
<input class="form-control-plaintext" name="yourcard" id="yourcard" type="text" placeholder="Enter your card number" readonly>
This one is useful if you want to display data but not let the user know it is a form field.
<label for="yournotes">Your notes</label>
<textarea class="form-control" name="yournotes" id="yournotes" rows="3"></textarea>
Like the input fields, the textarea always spans 100% of its parent container. You can adjust the height by changing the value of the rows
attribute.
<label for="yourfav">Pick one</label>
<select class="form-control" id="yourfav" name="yourfav">
<option>Select one</option>
<option value="1">Star Wars</option>
<option value="2">Star Trek</option>
<option value="3">Stargate</option>
<option value="4">Other</option>
</select>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="defaultCheck1">
<label class="form-check-label" for="defaultCheck1">
Default checkbox
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="defaultCheck2" disabled>
<label class="form-check-label" for="defaultCheck2">
Disabled checkbox
</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="inlineCheckbox1" value="option1">
<label class="form-check-label" for="inlineCheckbox1">1</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="inlineCheckbox2" value="option2">
<label class="form-check-label" for="inlineCheckbox2">2</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="inlineCheckbox3" value="option3" disabled>
<label class="form-check-label" for="inlineCheckbox3">3 (disabled)</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="exampleRadios" id="exampleRadios1" value="option1" checked>
<label class="form-check-label" for="exampleRadios1">
Default radio
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="exampleRadios" id="exampleRadios2" value="option2">
<label class="form-check-label" for="exampleRadios2">
Second default radio
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="exampleRadios" id="exampleRadios3" value="option3" disabled>
<label class="form-check-label" for="exampleRadios3">
Disabled radio
</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1">
<label class="form-check-label" for="inlineRadio1">1</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio2" value="option2">
<label class="form-check-label" for="inlineRadio2">2</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio3" value="option3" disabled>
<label class="form-check-label" for="inlineRadio3">3 (disabled)</label>
</div>
Now we will take the form field elements and put them into an attractive and useful layout. Bootstrap uses a slightly different version of its grid system for form layout.
Each row of fields is contained within a form-row
and each field is contained in a form-group
which also uses the grid col-{breakpoint}-{column-number}
grid class format. You can apply margins and padding to this grid in the same way you would a standard layout grid.
The coolest part of the form grid is that it functions the same as the layout grid - so your forms are instantly mobile-friendly!
<form>
<div class="form-row">
<div class="form-group col-xl-6">
<input type="text" class="form-control" placeholder="First name" name="firstName" id="firstName">
</div>
<div class="form-group col-xl-6">
<input type="text" class="form-control" placeholder="Last name" name="lastName" id="lastName">
</div>
</div>
<div class="form-row">
<div class="form-group col-xl-12">
<input type="email" class="form-control" placeholder="E-mail address" name="eMail" id="eMail">
</div>
</div>
<div class="form-row">
<div class="form-group col-xl-4">
<input type="text" class="form-control" placeholder="MAJCOM" name="Majcom" id="Majcom">
</div>
<div class="form-group col-xl-4">
<input type="text" class="form-control" placeholder="Department" name="Dept" id="Dept">
</div>
<div class="form-group col-xl-4">
<input type="text" class="form-control" placeholder="Rank" name="Rank" id="Rank">
</div>
</div>
</form>
You can use any permutation of the form-row
and form-group col-{size}-{number}
setup to construct an attractive form. The way I usually do this is to draw out the form on a hunk of paper first so I can visualize where things will go, create the layout, enter the inputs and tweak as needed.
Once you get a handle on using Bootstrap to validate your forms, I think you'll agree that it is pretty simple. At first - and if you try to follow their documentation - you might find it confusing.
The first thing I recommend is applying the class needs-validation
to your form tag. Then add the keyword novalidate
to the form tag. Now, be confused for a second....needs-validation...novalidate? Yes, they cancel out. The needs-validation
class tells Bootstrap to validate the form and novalidate
tells HTML5 not to validate the form.
To validate a field, add the required
keyword to the field then, beneath the field add a DIV
with the class invalid-feedback
containing the message you want displayed if the user doesn't fill out the field. You can also add a DIV
with the valid-feedback
class to provide a message if they do fill it in correctly. Personally, I rarely do this - but it changes asynchronously as the user corrects the fields they miss - which is kinda neat.
CFFORM
uses. Pay attention to your users and if, as happens sometimes, they have JavaScript disabled, you will want to perform server-side validation with ColdFusion. If this ever comes up let me know and I will give you my prototype using session
variables.
One last note about that JavaScript: You don't need to edit it in any way. What you see here works globally for any form on your page flagged with that needs-validation
class.
<form class="needs-validation" novalidate>
<div class="form-row">
<div class="col-md-6 mb-3">
<label for="validationCustom01">First name</label>
<input type="text" class="form-control" id="validationCustom01" value="" required>
<div class="valid-feedback">
Looks good!
</div>
<div class="invalid-feedback">
Please provide your first name.
</div>
</div>
<div class="col-md-6 mb-3">
<label for="validationCustom02">Last name</label>
<input type="text" class="form-control" id="validationCustom02" value="" required>
<div class="valid-feedback">
Looks good!
</div>
<div class="invalid-feedback">
Please provide your last name.
</div>
</div>
</div>
<div class="form-row">
<div class="col-md-6 mb-3">
<label for="validationCustom03">City</label>
<input type="text" class="form-control" id="validationCustom03" required>
<div class="invalid-feedback">
Please provide a valid city.
</div>
</div>
<div class="col-md-3 mb-3">
<label for="validationCustom04">State</label>
<select class="custom-select" id="validationCustom04" required>
<option selected disabled value="">Choose...</option>
<option>...</option>
</select>
<div class="invalid-feedback">
Please select a valid state.
</div>
</div>
<div class="col-md-3 mb-3">
<label for="validationCustom05">Zip</label>
<input type="text" class="form-control" id="validationCustom05" required>
<div class="invalid-feedback">
Please provide a valid zip.
</div>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="invalidCheck" required>
<label class="form-check-label" for="invalidCheck">
Agree to terms and conditions
</label>
<div class="invalid-feedback">
You must agree before submitting.
</div>
</div>
</div>
<button class="btn btn-primary" type="submit">Submit form</button>
</form>
<script>
// Example starter JavaScript for disabling form submissions if there are invalid fields
(function() {
'use strict';
window.addEventListener('load', function() {
// Fetch all the forms we want to apply custom Bootstrap validation styles to
var forms = document.getElementsByClassName('needs-validation');
// Loop over them and prevent submission
var validation = Array.prototype.filter.call(forms, function(form) {
form.addEventListener('submit', function(event) {
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
form.classList.add('was-validated');
}, false);
});
}, false);
})();
</script>
There are a few additional elements and oddities with using Bootstrap forms. Thankfully there are some simple work-arounds.
Yep - there are three possible sizes to your form fields. Small, default, and large - of course. The large fields come in handy if you know your app will be used on a mobile device. The small ones would be better used in an admin environment or in a layout where space is limited.
Setting a field to small or large is very easy. Just add the form-control-sm
or form-control-lg
class chained after the usual form-control
class and the size will change.
<input class="form-control form-control-lg" type="text" placeholder=".form-control-lg">
<input class="form-control" type="text" placeholder="Default input">
<input class="form-control form-control-sm" type="text" placeholder=".form-control-sm">
I think we can all agree that Bootstrap form fields are much prettier than normal fields. The one exception to that rule is the file browser field. Every browser has a different way of displaying this poor thing. Thankfully we can standardize it with Bootstrap!
<div class="custom-file">
<input type="file" class="custom-file-input" id="customFile">
<label class="custom-file-label" for="customFile">Choose file</label>
</div>
<script>
$('#customFile').on('change',function(){
//get the file name
var fileName = $(this).val();
//replace the "Choose a file" label
$(this).next('.custom-file-label').html(fileName);
})
</script>
You should note that there is a chunk of jQuery associated with this field. Without it the field will still work but the name of the file the user selects will not appear in the field. Add the script, edit the $('#customFile')
to be the ID
of your field and you are in business.
You can pretty-up the standard radio buttons and checkboxes using the custom-control-input
class.
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="customCheck1" name="mycheck" value="1">
<label class="custom-control-label" for="customCheck1">Check this custom checkbox</label>
</div>
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="customCheck2" name="mycheck" value="2">
<label class="custom-control-label" for="customCheck2">Check this custom checkbox</label>
</div>
<div class="custom-control custom-radio">
<input type="radio" id="customRadio1" name="customRadio" class="custom-control-input" value="1">
<label class="custom-control-label" for="customRadio1">Toggle this custom radio</label>
</div>
<div class="custom-control custom-radio">
<input type="radio" id="customRadio2" name="customRadio" class="custom-control-input" value="2">
<label class="custom-control-label" for="customRadio2">Or toggle this other custom radio</label>
</div>
To display custom radio buttons or checkboxes inline, just add the custom-control-inline
class to the containing DIV
.
<div class="custom-control custom-radio">
<input type="radio" id="customRadio1" name="customRadio" class="custom-control-input" value="1">
<label class="custom-control-label" for="customRadio1">Toggle this custom radio</label>
</div>
<div class="custom-control custom-radio">
<input type="radio" id="customRadio2" name="customRadio" class="custom-control-input" value="2">
<label class="custom-control-label" for="customRadio2">Or toggle this other custom radio</label>
</div>
One of the new things you see on the web these days is the "switch" style field. These are pretty neat and would really work well with allowing users to select a yes/no or on/off option. The switch is actuallty a skinned checkbox. As with any checkbox, if it's not checked then it doesn't exist when the form is submitted.
You can use my little JavaScript voodoo to change the label when the switch is checked.
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="customSwitch1">
<label class="custom-control-label" for="customSwitch1">Toggle this switch element</label>
</div>
<script>
$('#customSwitch1').change(function() {
//set a variable of the state of the switch - this returns true/false
var switchstate = $(this).prop('checked');
if(switchstate == true)
{document.getElementById("customSwitch1Label").innerHTML = "Yes, please notify me!";}
else if(switchstate == false)
{document.getElementById("customSwitch1Label").innerHTML = "Would you like to be notified?";}
})
</script>
This one is pretty simple. It adds the primary color of Bootstrap to the selected element and changes the arrow.
You can apply the size changes to the select boxes by adding custom-select-lg
or custom-select-sm
in chain with the custom-select
class.
<select class="custom-select">
<option selected>Open this select menu</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
Screen readers will have trouble with your forms if you don't include a label for every input. Since we are bound by the lovely Section 508 guidelines you must use - or start using if you haven't been - label
tags to correspond with your form fields.
Sometimes these labels get in the way. Here's an example
See how the username and password labels get in the way? Getting them out of there is simple! And no, you can't just remove the label text because the size and shape of the label still exists. Just add the class sr-only
to the label itself and it will only appear on screen readers.
This class can be used to hide ANYTHING you want to display to screen readers alone.
<form class="form-inline">
<label class="sr-only" for="inlineFormInputName2">Name</label>
<input type="text" class="form-control mb-2 mr-sm-2" id="inlineFormInputName2" placeholder="Jane Doe">
<label class="sr-only" for="inlineFormInputGroupUsername2">Username</label>
<div class="input-group mb-2 mr-sm-2">
<div class="input-group-prepend">
<div class="input-group-text">@</div>
</div>
<input type="text" class="form-control" id="inlineFormInputGroupUsername2" placeholder="Username">
</div>
<div class="form-check mb-2 mr-sm-2">
<input class="form-check-input" type="checkbox" id="inlineFormCheck">
<label class="form-check-label" for="inlineFormCheck">
Remember me
</label>
</div>
<button type="submit" class="btn btn-primary mb-2">Submit</button>
</form>
A couple of notes about the code above since it shows off two additional features you will find useful.
form-inline
added to the form
tag. Because we are displaying the form in a single line it does not need any form-row
or form-group
tags.
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">$</span>
</div>
<input type="text" class="form-control" aria-label="Amount (to the nearest dollar)">
<div class="input-group-append">
<span class="input-group-text">.00</span>
</div>
</div>
<label for="agree" class="sr-only"></label>
<div class="input-group mb-3">
<div class="input-group-prepend">
<div class="input-group-text">
<input type="checkbox" aria-label="Checkbox for following text input" required>
<div class="invalid-feedback pl-1">Please check the box.</div>
</div>
</div>
<label for="agreetext" class="sr-only"></label>
<input id="agreetext" type="text" class="form-control" aria-label="Text input with checkbox" placeholder="I agree with the terms and conditions" readonly>
</div>
This is one of those things that when you find out that it exists and how easy is it to use that it makes you kind of mad and excited at the same time. Constraint validation....this is actually an HTML5 thing that the W3 added. It allows you to validate fields against each other with a couple of lines of code. No jQuery or javascript needed.
Let's say you have a form wherein you want to make sure the user enter their e-mail address two times to make sure they get it right. Normally you would need to add a script to do this and it can be a huge mess. All you need to do with this new method is add an event to your form
tag.
Here's a simple example:
<form oninput="email2.setCustomValidity(email2.value != email1.value ? 'Email addresses do not match.' : '');">
<div class="form-row">
<div class="form-group col-xl-6">
<label for="email1">Enter your e-mail address</label>
<input type="email" class="form-control" placeholder="First name" name="email1" id="email1">
</div>
<div class="form-group col-xl-6">
<label for="email2">Enter your e-mail address again</label>
<input type="email" class="form-control" placeholder="Last name" name="email2" id="email2">
</div>
</div>
<input class="btn btn-primary" type="submit" value="Submit!">
</form>
Here's a quick breakdown of what this is doing....
oninput="email2.setCustomValidity(email2.value != email1.value ? 'Email addresses do not match.' : '');"
The oninput
event triggers when a user enters something into the specified input field. Then we name the second field by ID, "email2" and set a custom validity through HTML5 if the value of the second field isn't equal to the value of the first field. If you are really sharp you will recognize the syntax is the ternary operator value = value ? 'do this if true' : 'do this if false'
. So we check if the values are equal and if they aren't it adds a custom message to the field. Super simple!
That pretty much covers everything you need to implement Bootstrap forms into your apps. The snippets herein are perfect for snippets in your text editor too.