Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save marcus-at-localhost/3c71fc2b6066da88e7f74041daf9226e to your computer and use it in GitHub Desktop.
Save marcus-at-localhost/3c71fc2b6066da88e7f74041daf9226e to your computer and use it in GitHub Desktop.
HTMX and Bootstrap Toasts

HTMX and Bootstrap Toasts

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.

License.

<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" />
@roguefalcon
Copy link

This is clever. Good job.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment