I'm having trouble with my client-side form validation not working properly before the server-side validation kicks in. I've implemented Bootstrap 5.2 and followed the documentation by adding needs-validation
and novalidate
to the form.
Scenario:
When I enter a title that exceeds 50 characters in the event_name
input field, which has a max=50
set, the invalid-feedback
does not appear, and the form gets submitted despite failing the validation only to be caught by the server-side validation later.
Question:
Why is the client-side validation not taking effect?
Form:
<% layout('layouts/boilerplate') %>
<div class="row">
<h1 class="text-center">Setup</h1>
<div class="col-xl-8 offset-xl-2">
<form action="/events/new" method="POST" class="needs-validation" novalidate>
<h5 class="mt-2">Activation setup</h5>
<hr>
<label for="event[promotion_type]">Select which of your qr codes you'll use for this event</label>
<div class="btn-group-lg my-4 d-flex flex-wrap justify-content-center" role="group"
aria-label="Basic radio toggle button group" required>
<% for (let q of qr) { %>
<input type="radio" class="btn-check" onselect="setQrName" name="event[qr_name]" value="<%= q.name %>"
id="<%= q.name %>" autocomplete="off">
<label class="btn btn-outline-primary m-2" for="<%= q.name %>">
<%= q.name %>
</label>
<% } %>
</div>
<div class="invalid-feedback">
You must select a QR code
</div>
<small class="form-text text-muted">*Do not select a qr code that's mapped to another
activation. Manage qr codes on the <a href="/users/<%= user.id %>/activations">activations
page</a>.
</small>
<div class="row flex-row d-flex" style="margin-top: 4em;">
<div class="mb-2 col-6">
<label class=" form-label" for="event[event_start]">Start date & time*</label>
<input class="form-control" type="datetime-local" onchange="timeCheck()" id="eventStart"
name="event[event_start]" required>
<div class="invalid-feedback">
Must be filled out
</div>
</div>
<div class="mb-2 col-6">
<label class="form-label" for="event[event_end]">End date & time*</label>
<input class="form-control" type="datetime-local" onchange="timeCheck()" id="eventEnd"
name="event[event_end]" required>
<div class="invalid-feedback">
Must be filled out
</div>
</div>
<p id="timeValidate" style="color: red; font-style: italic;">
<%= %>
</p>
</div>
<div class="my-3">
<div class="mb-3">
<label class="form-label fw-medium" for="event_name">Activation title*</label>
<input class="form-control" placeholder='Ex, "Win 50% off!" or "1 in 10 win a free hat!"'
maxlength="50" type="text" id="event[event_name]" name="event[event_name]" required>
<div class="invalid-feedback">
Must be filled out
</div>
<small class="my-2">A short title that will get people excited to participate</small>
</div>
<div class="mb-3">
<button class="btn btn-primary"
onclick="update(document.getElementById('eventStart').value,document.getElementById('eventEnd').value)">Next</button>
</div>
</form>
<a href="/events">Back to all events</a>
</div>
</div>
Javascript validation from boilerplate.ejs
<script>
(function () {
'use strict'
// Fetch all the forms we want to apply custom Bootstrap validation styles to
var forms = document.querySelectorAll('.needs-validation')
// Loop over them and prevent submission
Array.prototype.slice.call(forms)
.forEach(function (form) {
form.addEventListener('submit', function (event) {
if (!form.checkValidity()) {
event.preventDefault()
event.stopPropagation()
}
form.classList.add('was-validated')
}, false)
})
})()
</script>
Note:
As mentioned by @carolskelly in the comments, the required
validation still functions correctly preventing the submission of blank input fields.