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:
- the
collapsed
class will be absent on the menu toggler button’s class list - the
show
class will be present on the dropdown menu element’s class list
The result of this menu being left in an opened state is not ideal and is problematic in terms of user experience — definitely a bug. Since fixing this behavior requires reading from the browser’s 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.
Creating the Close Function
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
id
attribute. I’ve manually addedid="navbarToggler"
to the<button>
element in order to utilize thedocument.getElementById()
method.
Executing the Close Function on Page Load
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 hooks.js
endpoint.
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!