Using Bootstrap Validation with HTMX as Extension
See gist: https://gist.github.com/marcus-at-localhost/c9f0a34a2e19d666fd95f61f002ea516
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/htmx.min.js"></script>
<div class="container my-5">
<form
hx-post="/foo"
hx-ext="bs-validation"
class="row g-3 needs-validation"
novalidate
>
<div class="col-md-4">
<label for="validationCustom01" class="form-label">First name</label>
<input type="text" class="form-control" id="validationCustom01" value="Mark" required>
<div class="valid-feedback">
Looks good!
</div>
</div>
<div class="col-md-4">
<label for="validationCustom02" class="form-label">Last name</label>
<input type="text" class="form-control" id="validationCustom02" value="Otto" required>
<div class="valid-feedback">
Looks good!
</div>
</div>
<div class="col-md-4">
<label for="validationCustomUsername" class="form-label">Username</label>
<div class="input-group has-validation">
<span class="input-group-text" id="inputGroupPrepend">@</span>
<input type="text" class="form-control" id="validationCustomUsername" aria-describedby="inputGroupPrepend" required>
<div class="invalid-feedback">
Please choose a username.
</div>
</div>
</div>
<div class="col-md-6">
<label for="validationCustom03" class="form-label">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">
<label for="validationCustom04" class="form-label">State</label>
<select class="form-select" id="validationCustom04" required>
<option selected disabled value="">Choose...</option>
<option>...</option>
</select>
<div class="invalid-feedback">
Please select a valid state.
</div>
</div>
...
JavaScript
// https://gist.github.com/marcus-at-localhost/c9f0a34a2e19d666fd95f61f002ea516
htmx.defineExtension('bs-validation', {
onEvent: function (name, evt, data) {
//if (name !== "htmx:afterProcessNode") {
// return;
//}
console.log('event ' + name);
let form = evt.detail.elt;
// check if trigger attribute and submit event exists
// for the form
if(!form.hasAttribute('hx-trigger')){
// set trigger for custom event bs-send
form.setAttribute('hx-trigger','bs-send');
// and attach the event only once
form.addEventListener('submit', function (event) {
if (form.checkValidity()) {
// trigger custom event hx-trigger="bs-send"
htmx.trigger(form, "bsSend");
console.log('bsSend')
}
console.log('prevent')
event.preventDefault()
event.stopPropagation()
form.classList.add('was-validated')
}, false)
}
}
});