When using SvelteKit paired with the Bootstrap framework, it’s normal to for my default
__layout to look something like this:
In the above markup, the
<Header/> component contains HTML for the navigation menu. SvelteKit by default hydrates pages by fetching the content between the
<slot> tags, enabling instant navigation and a great user experience.
But because of this hydration, the
<Header/> component is never reinstantiated when a new page is navigated to, and what I’ve found is that Bootstrap’s mobile navigation menu is left in an opened state after page navigation occurs.
A few classes and attributes are mutated when the navigation toggle is clicked, namely the menu toggler button and the dropdown menu element:
If the navigation menu is left in an opened state after a new page is loaded, then:
collapsedclass will be absent on the menu toggler button’s class list
showclass will be present on the dropdown menu element’s class list
document variable, Svelte offers a way to run code after a page has been rendered using the
onMount feature. This allows the DOM to be safely traversed when client-side events need to be triggered, and is a the perfect way to run a check on the navigation menu upon each page load.
Firstly, I created the reusable logic and placed it in an endpoint under
$lib so that it can be be easily imported:
The above code reads the dropdown menu element’s class list and removes the
show class if present, thereby hiding the menu and returning it to the default closed state.
The above code also reads the menu toggler button’s class list. If the
collapsed class is not present in the list then the function simply toggles it, returning it to the default closed state. For accessibility purposes, it also sets the
aria-expanded attribute to
false when closing the menu.
It’s important to note that by default in Bootstrap, the menu toggler button does NOT have an
idattribute. I’ve manually added
<button>element in order to utilize the
On each Svelte route (page) the function is imported from the
nav.js endpoint and is invoked in
onMount once the DOM has been hydrated:
Since this happens within the context of the Svelte’s routing, there isn’t any visible delay. Thus, Bootstrap’s non-hydrated menu state can be updated from the hydrated page content directly.
Full disclosure: there’s probably other (and more elegant) ways to achieve this, for example using Bootstrap’s native
Collapse methods, or perhaps via Svelte’s
At the end of the day this method worked for me and fixed the problem so that I could move on. If you discover or know of a better approach, feel free to let me know in the comments!