Just a little demo how to use bootstraps toasts component with HTMX and custom triggers. Toasts are stackable and grouped by message (so the same message doesn't pop up several times, when already open.
A Pen by Marcus at Localhost on CodePen.
Just a little demo how to use bootstraps toasts component with HTMX and custom triggers. Toasts are stackable and grouped by message (so the same message doesn't pop up several times, when already open.
A Pen by Marcus at Localhost on CodePen.
<div class="container my-5"> | |
<div class="justify-content-center d-flex gap-3"> | |
<button class="btn btn-primary" hx-get="https://run.mocky.io/v3/60014a97-412f-407e-b47f-6e955e67e220"> | |
π Action | |
</button> | |
<button class="btn btn-primary" hx-get="https://run.mocky.io/v3/5f18f6c6-c36e-496a-b63b-0236b47ae5a2"> | |
π More Action | |
</button> | |
<button class="btn btn-primary" hx-get="https://run.mocky.io/v3/4c9b0042-26df-4bd8-8ab3-e7510eee40b5" hx-target="toaster"> | |
π Even More Action | |
</button> | |
</div> | |
<toaster></toaster> | |
</div> | |
<div class="toast-container position-fixed bottom-0 start-50 translate-middle-x"></div> | |
<template id="toastTemplate"> | |
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true"> | |
<div class="d-flex"> | |
<div class="toast-body"></div> | |
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button> | |
</div> | |
</div> | |
</template> |
const hashCode = s => s.split('').reduce((a,b)=>{a=((a<<5)-a)+b.charCodeAt(0);return a&a},0); | |
const toast = function(msg, mode){ | |
const template = document.querySelector('#toastTemplate'); | |
const clone = template.content.firstElementChild.cloneNode(true); | |
clone.classList.add(`text-bg-${mode ?? 'secondary'}`); | |
const group = Math.abs(hashCode(msg)); | |
clone.dataset.group = group; | |
clone.querySelector('.toast-body').innerHTML = msg; | |
const toast = new bootstrap.Toast(clone, {autohide: false}); | |
const container = document.querySelector('.toast-container'); | |
const toasts = container.querySelectorAll('.hide'); | |
toasts.forEach(toast => container.removeChild(toast)); | |
const groupsToShow = container.querySelectorAll(`[data-group="${group}"].show`); | |
if(groupsToShow.length) { | |
return; | |
} | |
container.appendChild(clone); | |
toast.show(); | |
} | |
htmx.on('htmx:responseError', event => toast(event.detail.xhr.responseText,'danger')); | |
htmx.on('ButteredToast', event => toast(event.detail.value, 'success')); |
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5/dist/js/bootstrap.bundle.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/htmx.org@1/dist/htmx.min.js"></script> |
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5/dist/css/bootstrap.min.css" rel="stylesheet" /> |
This is clever. Good job.