Open and close <dialog>
modals based on the URL hash value. A vanilla JavaScript library for all browsers and frameworks. Demo
2023-03-07.16.46.29.1.mp4
Assume a webpage with the following anchor elements.
<a href="#1">#1</a>
<a href="#2">#2</a>
The dialog will be opened if
- The page is initially loaded with the hash of
#1
or#2
. - Hash change is triggered (e.g. clicking
<a href="https://accionvegana.org/accio/0ITbvNmLiVHa0l2Z6MHc0/#1">
).
The dialog will be not open if
- The page is requested with
#3
, since there are no<a>
elements with thehref
value of#3
. The URL hash is trimmed as well.
The dialog will be closed if
- The
Escape
key is pressed (browser default). dialog.close()
is called using JavaScript.
When the dialog is closed
- Hash is removed from the current URL using
history.pushState()
. - Corresponding
<a>
element is focused for keyboard accessibility.
When the dialog is opened/closed
- The
<body>
can have itsoverflow
automatically set tohidden/visible
to remove scroll from the<body>
. Reference options.
Import from jsDelivr - Vanilla JavaScript example
<a href="#1">#1</a>
<a href="#2">#2</a>
<dialog></dialog>
<script type="module">
import { controlDialogWithUrlHash } from 'cdn-of-choice';
// Select the dialog element.
const dialog = document.querySelector('dialog');
const updateDialogContent = () =>
(dialog.textContent = `Current hash is '${window.location.hash}'`);
updateDialogContent(); // Update the dialog content on initial load.
// Dynamically update the dialog content based on the URL hash change.
window.addEventListener('hashchange', updateDialogContent);
// Pass the dialog element to the imported function.
const { removeEventListeners } = controlDialogWithUrlHash(dialog, 'auto');
// Remove all event listeners when needed.
// - window.removeEventListener('hashchange', updateDialogContent);
// - removeEventListeners();
</script>
Install from npm - Node.js and SvelteKit example
<script>
import { page } from '$app/stores';
import { controlDialogWithUrlHash } from 'hash-dialog-modal';
/** @type {import('svelte/action').Action<HTMLDialogElement>} */
const controlWithHash = (dialog) => {
const { removeEventListeners } = controlDialogWithUrlHash(dialog, 'auto');
return { destroy: removeEventListeners };
};
</script>
<a href="#1">#1</a>
<a href="#2">#2</a>
<!-- Requires @sveltejs/kit@1.22.0+ -->
<dialog use:controlWithHash>Current hash is {$page.url.hash}</dialog>
export declare const controlDialogWithUrlHash: (
dialog: HTMLDialogElement,
overflow: 'auto' | 'manual', // sets the overflow style of the <body>
options?: Partial<{
onHashRemoval: () => void; // runs on dialog close and hash removal
}>
) => {
removeEventListeners: () => void; // removes all event listeners
};
What overflow
auto is recommended.
// Remove scroll from the <body> when the <dialog> is opened as a modal.
if (overflow === 'auto') document.body.style.overflow = 'hidden'; // handleHash
if (overflow === 'auto') document.body.style.overflow = 'visible'; // closeDialog
Why onHashRemoval
callback should be provided.
window.history.pushState(null, '', ' '); // Does not trigger on:hashchange event.
options.onHashRemoval?.(); // Therefore, the callback should be manually provided.