I have offcanvas as part of the page layout. It does not show by default, but I want it to always show on large screens. On small screens it should have a button to dismiss it. Another button to show the offcanvas is placed on the menu panel underneath it.
I'm using JavaScript on page load and on page resize to show the offcanvas when if screen is large. If the user then scales the screen down, the offcanvas should remain visible until dismissed.
The issue is that the dismiss button works on the offcanvas only if it has not been initialised via JS. I.e. if opened on a small screen, the offcanvas is not visible and the two bottons can be used to toggle back and forth. But once the offcanvas has been triggerd via JS on a large screen, the dismiss button no longer works when the screen is downsized. I'm struggling to figure out if it has lost an EventListener and how to add it back, if so.
Layout:
<!-- Button to show offcanvas (positioned underneath it) -->
<div>
<button class="btn" type="button" id="off-canvas-body-toggle" data-bs-toggle="offcanvas" data-bs-target="#floating-menu">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#422543" class="bi bi-arrow-bar-right" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M6 8a.5.5 0 0 0 .5.5h5.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3a.5.5 0 0 0 0-.708l-3-3a.5.5 0 0 0-.708.708L12.293 7.5H6.5A.5.5 0 0 0 6 8Zm-2.5 7a.5.5 0 0 1-.5-.5v-13a.5.5 0 0 1 1 0v13a.5.5 0 0 1-.5.5Z" />
</svg>
</button>
</div>
<!-- Offcanvas -->
<div class="offcanvas offcanvas-start" data-bs-scroll="true" data-bs-backdrop="false" tabindex="-1" id="floating-menu">
<!-- Bootstrap classes d-xl-none d-lg-none hides the div with dismiss button on large screen -->
<!-- But it becomes visible when the screen is downsized -->
<div class="offcanvas-header d-xl-none d-lg-none">
<!-- Dismiss button that does not work corectly -->
<button type="button" class="btn" id="off-canvas-body-dismiss" data-bs-dismiss="offcanvas">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#422543" class="bi bi-arrow-bar-left" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M12.5 15a.5.5 0 0 1-.5-.5v-13a.5.5 0 0 1 1 0v13a.5.5 0 0 1-.5.5ZM10 8a.5.5 0 0 1-.5.5H3.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L3.707 7.5H9.5a.5.5 0 0 1 .5.5Z" /></svg>
</button>
</div>
<div class="offcanvas-body">
<!-- This contains an accordion and works fine -->
</div>
</div>
JS:
// Once the HTML has loaded
document.addEventListener("DOMContentLoaded", function () {
// If on large screen, show offcanvas upon page load
adjust_layout();
// On resize - if large screen then always show offcanvas
addEventListener("resize", adjust_layout);
}
function adjust_layout() {
// Get the Div that contains the offcanvas
const menu = document.getElementById("floating-menu");
var bsOffcanvas = new bootstrap.Offcanvas(menu);
// If large screen - show the offcanvas
let width = window.innerWidth;
if (width >= 992) {
bsOffcanvas.show();
}
}
As you're creating a new instance on every resize, most likely newly created JavaScript instance interferes somehow with data attribute instance (or I guess dismiss button doesn't work if
.show
method wasn't called on newly created instance...).So, use
getOrCreateInstance
method instead, to get the same offcanvas instance for that DOM element:instead of:
use:
or shorter:
See:
Also, your code seems to work if you create the instance only once, and not on every resize, i.e. if you move it outside the
adjust_layout
function like so, but I'd usegetOrCreateInstance
method instead to make sure both data and JS parts work as expected, so here's the final suggested code (in any case, you should create the instance only once, not on every resize):