Archived topic
Prevent body scroll for modals/ overlays
19 replies · Started by Dorin on November 16, 2022
I'm in need of a solution that prevents body scrolling when modals are open on every device, os and web browser. It seems CSS only can't achieve what I'm after. I stumbled upon this script that does exactly that. Now I tried loading the script via the cdn files using a hook element. My code looks something like this (I targeted the off canvas menu) - but it doesn't work:
<script src="https://cdnjs.cloudflare.com/ajax/libs/body-scroll-lock/3.1.5/bodyScrollLock.min.js" integrity="sha512-HowizSDbQl7UPEAsPnvJHlQsnBmU2YMrv7KkTBulTLEGz9chfBoWYyZJL+MUO6p/yBuuMO/8jI7O29YRZ2uBlA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/body-scroll-lock/3.1.5/bodyScrollLock.es6.min.js" integrity="sha512-HCiqK+5xnhw9WYQN+BP6yKFr6iZ+euY5CtCVw/q//BLb3X7xsYNYB+/cCWhzE1m39WfDaXnaNZeDa4++lMKHtQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/body-scroll-lock/3.1.5/bodyScrollLock.esm.min.js" integrity="sha512-1D7ifQ1x2RpCxn+WK5i9bAUQ4KlK2BETBSgMOf+clTopmDVxkBBXcUgK7UA5LQ4kcgvqEcxLlnwvVQq1fgIPfw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
const bodyScrollLock = require('body-scroll-lock');
const disableBodyScroll = bodyScrollLock.disableBodyScroll;
const enableBodyScroll = bodyScrollLock.enableBodyScroll;
const targetElement = document.querySelector('#generate-slideout-menu');
disableBodyScroll(targetElement);
enableBodyScroll(targetElement);
</script>
I know it's not a css specific question, but any idea on how to achieve a proper body scroll lock when a modal is open is greatly appreciated. Most of my pages use overlay modals (I'm leaving an example). I'm just trying to enhance the user experience, especially on mobile.
Hi there,
is it just for the Off canvas ? if so - what about this CSS:
.slide-opened body {
max-height: 100vh;
overflow: hidden;
}
.slide-opened #generate-slideout-menu {
overflow: scroll;
}
Works well, David. Thanks. Do you think I can make it work for the modals in the page I left privately? That's my main issue right now.
Ok, its possible but not in its present state.
Where does the modal script come from? As currently it simply changes an inline style to show the modal, and we need it to toggle a class on the body or better still the html element.
The script sits below the html in the custom html block on the page. I took the code from this codepen.
Ok, so thats good as you can edit it.
At the top of the script add:
var rootHTML = document.getElementsByTagName( 'html' )[0]
This will load the HTML element into the rootHTML variable.
Then in your script there are two click events:
modal_btn_multi[i].onclick = function() {
var ElementIndex = this.getAttribute('data-index');
modalparent[ElementIndex].style.display = "block";
};
// When the user clicks on <span> (x), close the modal
span_close_multi[i].onclick = function() {
var ElementIndex = this.getAttribute('data-index');
modalparent[ElementIndex].style.display = "none";
};
change them to:
modal_btn_multi[i].onclick = function() {
var ElementIndex = this.getAttribute('data-index');
modalparent[ElementIndex].style.display = "block";
rootHTML.classList.add('modal-open');
};
// When the user clicks on <span> (x), close the modal
span_close_multi[i].onclick = function() {
var ElementIndex = this.getAttribute('data-index');
modalparent[ElementIndex].style.display = "none";
rootHTML.classList.remove('modal-open');
};
This should now toggle the modal-open class on the HTML Element.
And then our CSS becomes:
.slide-opened body,
.modal-open body {
max-height: 100vh;
overflow: hidden;
}
.slide-opened #generate-slideout-menu,
.modal-open .modal {
overflow: scroll;
}
Made all the updates, but now the modals are not opening.
OK,
reverse the changes to the Javascript.
keep the CSS.
Now add this script to your footer:
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutationRecord) {
rootHTML.classList.toggle('modal-open');
});
});
var rootHTML = document.getElementsByTagName( 'html' )[0];
var modals = document.querySelectorAll('.modal');
modals.forEach(modal => {
observer.observe(modal, {
attributes: true,
attributeFilter: ['style']
});
});
This should observe any changes to the modal elements styles and add the modal-open class to the HTML node.
Still not working unfortunately. Sorry to bug you with this issue.
I can see the Class being toggled on the head, so the JS is working.
Did you change the CSS to:
.slide-opened body,
.modal-open body {
max-height: 100vh;
overflow: hidden;
}
.slide-opened #generate-slideout-menu,
.modal-open .modal {
overflow: scroll;
}
Yes, that code is in place. It's working for the offcanvas menu, but not for the rest of the modals.
You have this CSS for the tablet:
/* Tablet view */
@media(max-width: 1024px) and (min-width: 769px) {
.separate-containers .inside-article, .inside-header, .inside-site-info {
padding-left: 20px;
padding-right: 20px;
}
.swiper-button-next, .swiper-button-prev {
display: none;
}
body .main-navigation.offside {
width: 50%;
}
Is missing a closing } for the @media query.
It should be:
/* Tablet view */
@media(max-width: 1024px) and (min-width: 769px) {
.separate-containers .inside-article, .inside-header, .inside-site-info {
padding-left: 20px;
padding-right: 20px;
}
.swiper-button-next, .swiper-button-prev {
display: none;
}
body .main-navigation.offside {
width: 50%;
}
}
Without that, the CSS below it is being included inside that media query
Brilliant stuff, David. It's working now. Sorry about forgetting to close that piece of code.
The only area where it seems to not work everytime (on some modals it's working, on others it doesn't, espcially the last one in the list) is on Safari for mobile. What happens is the vertical scrollbar of the body remains visible sometimes and that triggers the scroll. Any chance to hide the vertical scrollbar of the body?
Hmmm... tricky one.
What if we move the ovrflow scroll to the modal-content in the CSS ie.
.slide-opened body,
.modal-open body {
max-height: 100vh;
overflow: hidden;
}
.slide-opened #generate-slideout-menu,
.modal-open .modal-content {
overflow: scroll;
}
The same behaviour. Sometimes works, other times the scroll gets activated. Some resources on the web suggest adding position: fixed; to the body element when the modal is open. Don't know exactly how to implement it.