Lesson 8 - Bootstrap - Advanced forms
In the previous lesson, Bootstrap - Forms, we started forms in Bootstrap. In today's CSS tutorial, we'll continue and also finish this topic. There is client-side validation, custom styles, and input groups waiting for us up ahead.
Validation
Another strength of Bootstrap forms are client-side validations. If we define
them for form controls, Bootstrap helps us to highlight the invalid controls and
shows error messages next to them. For this, the :invalid
and
:valid
pseudo-classes are used internally, highlighting the form
controls if the .was-validated
class is assigned to the form. The
class is assigned automatically by Bootstrap when the form is submitted.
In the example below, we're showing custom messages in client-side
validation. Before we can do that, we need to turn off the default browser's
messages like "Please fill out this field." by adding the
novalidate="novalidate"
attribute to the <form>
element. We're going to use a custom JavaScript function to render the
messages.
<form id="needs-validation" novalidate="novalidate"> <div class="form-group"> <label for="form-name">Name</label> <input type="text" class="form-control" id="form-name" placeholder="John Doe" required="required"> <div class="invalid-feedback"> Please enter your full name. </div> </div> <div class="form-group"> <label for="form-email">Email</label> <input type="text" class="form-control" id="form-email" placeholder="[email protected]" required="required"> <div class="invalid-feedback"> Please enter a valid email address. </div> </div> <button class="btn btn-primary" type="submit">Send</button> </form> <script> (function() { 'use strict'; window.addEventListener('load', function() { var form = document.getElementById('needs-validation'); form.addEventListener('submit', function(event) { if (form.checkValidity() === false) { event.preventDefault(); event.stopPropagation(); } form.classList.add('was-validated'); }, false); }, false); })(); </script>
The result:
If you omit the JavaScript part, the messages will be rendered only one at a
time by the browser and wouldn't be shown all at once. Let's try the same code
without the script and the novalidate
attribute.
Server-side validation
If we wanted to highlight the form like this even when performing the
server-side validation, we'd just assign the .is-valid
and
.is-invalid
classes to its controls. In this situation, the form
doesn't have to have the .was-validated
class anymore.
We should always perform the server-side validation, because the client-side validation can never be trusted!** The client-side validation just makes the user experience better since they get error messages before the form is even sent to the server.
We can also alter the client-side validation in JavaScript, see the
setCustomValidity()
method.
Custom styles
The appearance of checkboxes, radio buttons, file inputs, and selects is
often adopted from the operating system or the default browser settings, which
doesn't always look nice. That's why Bootstrap allows to style these controls
completely on our own. This is internally achieved by hiding them via
opacity: 0
. We'll assign the .custom-control
class to
the <label>
element and add the new controls in place of the
hidden ones as <span>
elements with the
.custom-control-indicator
and
.custom-control-description
classes.
A form with custom control styles would have the following source code:
<form> <div class="form-check"> <label class="custom-control custom-checkbox"> <input class="custom-control-input" type="checkbox" value=""> <span class="custom-control-indicator"></span> <span class="custom-control-description">I agree to the Terms of Service</span> </label> </div> <div class="form-check"> <label class="custom-control custom-radio"> <input class="custom-control-input" type="radio" name="payment-method" id="payment-method1" value="card" checked> <span class="custom-control-indicator"></span> <span class="custom-control-description">Credit Card</span> </label> </div> <div class="form-check"> <label class="custom-control custom-radio"> <input class="custom-control-input" type="radio" name="payment-method" id="payment-method2" value="wiretransfer" checked> <span class="custom-control-indicator"></span> <span class="custom-control-description">Wiretransfer</span> </label> </div> <div class="form-group"> <label for="form-select">Size</label> <select class="custom-select" id="form-select"> <option>S</option> <option>M</option> <option>L</option> <option>XL</option> </select> </div> <div class="form-group"> <label class="custom-file"> <input type="file" class="custom-file-input"> <span class="custom-file-control"></span> </label> </div> </form>
For checkboxes, we assign the .custom-checkbox
class to the
<label>
elements and the .custom-control-input
class to the <input>
s. Checkboxes also support the
indeterminate
property which makes the checkbox is neither checked
or unchecked (the dash -
symbol is rendered on it). The property
has to be eventually set via JavaScript. For radiobuttons, we assign the
.custom-radio
class to the <label>
elements and
.custom-control-input
to the <input>
s. For
selects, we assign the .custom-select
class. And for file inputs,
we assign the .custom-file-input
class and add a
<span>
with the .custom-file-control
class.
The result:
If you wanted to translate the text on the file input, it depends on the
language defined for the page in the lang
attribute of the
<html>
element. The texts are taken from the Bootstrap's SASS
source codes, specifically the $custom-file-text
variable.
Unfortunately, the default build renders custom file inputs only for the
en
language.
Custom controls will align next to each other. If we want them
one under another, we have to put them in divs as shown in the example above. We
used the .form-group
class here. Bootstrap also provides the
.custom-controls-stacked
class, if we used it, we won't have to put
controls in individual divs, we could put them in a single
<div>
with this class assigned.
Input groups
In the Bootstrap CSS framework, input groups allows us to insert other elements, such as texts or buttons, on the sides of text inputs. These extensions are called addons. We have already seen such inputs in the lesson about forms, but never described them in detail. Let's show a basic example:
<div class="form-group"> <div class="input-group"> <span class="input-group-addon" id="address-addon">ict.social/</span> <input type="text" class="form-control" placeholder="article name" aria-label="Article name" aria-describedby="address-addon"> </div> </div> <div class="form-group"> <div class="input-group"> <input type="text" class="form-control" placeholder="Your name" aria-label="Your name" aria-describedby="email-addon"> <span class="input-group-addon" id="email-addon">@ict.social</span> </div> </div>
We insert the <input>
in a <div>
with
the .input-group
class. We put the text on the left or right side
of the <input>
using the <span>
element
with the .input-group-addon
class. Another text input controls or
labels are not supported.
The result:
Sizes
To change the <input>
size we assign one of the following
classes to the <div>
element with the
.input-group
class.
.input-group-lg
for a large size- None for the standard size
.input-group-sm
for a small size
Other addons
We can also put multiple add-ons next each other on the sides. Except for the text addons shown above, we can also add checkboxes, radio buttons, buttons, and dropdowns. Let's show another example of advanced addons.
<div class="input-group"> <span class="input-group-addon" id="checkbox-addon"> <input type="checkbox" aria-label="Checkbox"> </span> <span class="input-group-addon" id="text-addon">Text</span> <span class="input-group-btn"> <button class="btn btn-secondary" type="button">Button</button> </span> <input type="text" class="form-control" aria-label="Sample input"> <div class="input-group-btn"> <button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> Dropdown </button> <div class="dropdown-menu"> <a class="dropdown-item" href="#">Item 1</a> <a class="dropdown-item" href="#">Item 2</a> <div role="separator" class="dropdown-divider"></div> <a class="dropdown-item" href="#">Item 3</a> </div> </div> </div>
For correct functionality of dropdown buttons, don't forget to link the Bootstrap's JavaScript. The result in the browser:
We can also segment dropdowns, i.e. divide them into 2 separate buttons where the larger button works independently and the smaller button drops the options.
<div class="input-group-btn"> <button type="button" class="btn btn-secondary">Dropdown</button> <button type="button" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <span class="sr-only">Toggle dropdown</span> </button> <div class="dropdown-menu dropdown-menu-right"> <a class="dropdown-item" href="#">Item 1</a> <a class="dropdown-item" href="#">Item 2</a> <div role="separator" class="dropdown-divider"></div> <a class="dropdown-item" href="#">Item 3</a> </div> </div>
The result:
If you wanted the dropdown on the left side, don't assign the
.dropdown-menu-right
class to the menu. Now, we've finished the
forms Next time, in the lesson
Bootstrap - Jumbotron and Badges, we'll discuss Jumbotron component and learn how to use Bootstrap
badges.