How to set element to height 0px? - css

I want to make my element dynamically change its height from initial to 0px but it seems to be not working because of padding. I cannot say what I am doing wrong.
I do not want use: display: block or visibility hidden. I do not want to remove padding from styles when height is 0 because it makes this logic too complicated. I hope that I am doing sth stupid and easy to fix here and it is why it is not working. Any ideas?
I am adding it for more context. It is React. I've simplified code - but it is basic idea. And I want to make this modal resizable. So I have :
const StyledModal = styled.div<ModalProps>`
box-sizing: border-box;
position: fixed;
z-index: 9000;
background-color: white;
height: ${({ height }) => `${height}px`};
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 100%;
padding: 3.4rem 0 3.2rem 0;
`
const Modal = ({children}) => {
const [height, setHeight] = useState(0)
useEffect(() => {
setHeight(ref &&
ref.current &&
ref.current.getBoundingClientRect().height)
}, [])
const reduceHeight = () => setHeight(0)
return <StyledModal
onClick={reduceHeight}
height={height}>{children}</StyledModal>
}
Problem is that even if I manually set height to 0px in web developer tools , this element exists because of this padding like in the screenshot above.

Related

How to add media breakpoints dynamically in Typescript style components using React Hooks

I need height > width for mobile devices, for that i removed the aspectRatio from index.js in handleResize function inside src/camera folder and somehow it worked. But i feel it's a kindof hacky thing to do. Can any one suggest how can i solve my problem for different media breakpoints.
Below is my sandbox code, which i borrowed from Andrews James's post :
https://blog.logrocket.com/responsive-camera-component-react-hooks/
https://codesandbox.io/s/react-camera-component-s0uqfr
Edit: Specific Problem
const [container, setContainer] = useState({ width: 0, height: 0});
const [aspectRatio, calculateRatio] = useCardRatio(0.586);
function handleResize(contentRect) {
setContainer({
width: contentRect.bounds.width,
height: Math.round(contentRect.bounds.width / aspectRatio)
// height: Math.round(contentRect.bounds.width)
});}
css Style:
export const Container = styled.div`
position: relative;
width: 100%;
max-width: ${({ maxWidth }) => maxWidth && `${maxWidth}px`};
max-height: ${({ maxHeight }) => maxHeight && `${maxHeight}px`};
overflow: hidden;
`;
Now the height property gets fixed regardless of any resolution in chrome devtools dimensions.
Thanks in Advance.
You can use css variables.
document.style.setProperty("--my-breakpoint", myValue);
with that media query
#media(max-width: var(--my-breakpoint)) {
// anything you want
}

How to go about adding a sliding animation for a toggle button?

I have a toggle bar element I am working on and currently I have it working correctly to style the toggled button, but I want more animation.
The idea is to have the curent active button slide to whichever button is newly active. So visually the only thing I would be adding to the current code is the slide animation.
How to do that exactly is where I'm getting stuck. Now the active element isn't just getting a background and some styles, but a floating/sliding box will be needed (I think)
I don't want anyone to impliment it for me, but I am having trouble thinking of how to impliment it. Do I do something with a pseudoelements?
How do I make sure that the white button background element is as wide as the word it needs to be behind?
This is more or less a general CSS question, but I am not sure how to even start this process.
If anyone has any advice or tricks on how to make this work or just pointing me in the right direction, I would greatly appreciate it! Cheers!
This is what I've built so far in CodeSandbox
You could 'create a gap' for the white block to travel in so it is behind the text but in front of the backgrounds by putting the background colors onto the elements using pseudo elements with lower z index.
On click of a button you can look at the previously clicked button and work out how far the white block has to move to get to the current button, and also how its width has to change.
In this snippet the 'white block' is in fact a pseudo element on the currently clicked button. This makes it easy to work out its final resting place which is directly under the currently clicked button.
const buttons = document.querySelectorAll('button');
function clicked(e) {
const el = e.target;
const prevEl = document.querySelector('button.animate');
const x = (prevEl == null) ? '0%' : (prevEl.getBoundingClientRect().x - el.getBoundingClientRect().x) + 'px';
const w2 = window.getComputedStyle(el).width;
const w1 = (prevEl == null) ? 0 : window.getComputedStyle(prevEl).width;
buttons.forEach(button => {
button.classList.remove('animate');
});
el.style.setProperty('--x', x);
el.style.setProperty('--w1', w1);
el.style.setProperty('--w2', '100%');
el.classList.add('animate');
}
buttons.forEach(button => {
button.addEventListener('click', clicked);
}
);
nav {
display: inline-block;
position: relative;
}
nav::before {
content: '';
position: absolute;
background-color: gold;
width: 100%;
height: 100%;
left: 0 top: 0;
z-index: -3;
}
button {
margin: 2vmin;
background-color: transparent;
position: relative;
}
button::before {
content: '';
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-color: brown;
z-index: -2;
}
button.animate::after {
content: '';
position: absolute;
background-color: white;
width: 100%;
height: 100%;
animation-name: move;
animation-duration: 3s;
animation-fill-mode: forwards;
top: 0;
left: 0;
z-index: -1;
}
#keyframes move {
0% {
transform: translateX(var(--x));
width: var(--w1);
}
100% {
transform: translateX(0);
width: var(--w2);
}
}
#keyframes animate {
0% {}
100% {
background-color: white;
}
}
<nav>
<button>AAAA</button>
<button>AA</button>
<button>AAAAAA</button>
<button>A</button>
</nav>
If you want to do it from scratch, and have the ability to control the position of the white box relative to the yellow box (position: relative or absolute), you could hardcode it by creating a function that takes the desired box coordinate, checks if the white box is in the correct place, and if not moves it x px in the right direction, then calls itself recursively with a timeout.
Essentially, move it a little bit every few milliseconds until it gets to the right spot. one move every (1000/24)ms or less should make it look fluid.
As for the size, you could either hardcode that and same idea, or make a function that finds the correct size based on the given text.

Weird behavior of animating css using scroll event on legacy browsers except firefox

I'm working on a simple gallery using css grid and I'm trying to animate gaps between the items when the user scrolls down. But I don't know why I get this weird behavior on chrome and edge which is every item just shrinks by height when I try to scroll. It works fine in firefox and everything under the hood seems to work fine and i get no error whatsoever and I tried autoprefixer on my css but it didn't help in any way.
<div class="container" id="container">
...images goes here (sm,md,lg classes)
</div>
.container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
grid-auto-rows: minmax(10%, 20%);
grid-auto-flow: dense;
transition: all 1s;
transition-delay: 0.1s;
gap: 1rem;
padding: 0 1rem;
}
img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 2px;
}
.sm {
grid-row: span 1;
}
.md {
grid-row: span 5;
}
.lg {
grid-row: span 6;
}
.scroll-animation {
gap: 0.1rem;
padding: 0 0.1rem;
}
let lastScrolled = 0; // Number of pixels the user has scrolled in the last event
// Basically we want to add "scroll-animation" to the container when the user scrolled down
// This state controlls event calls
let fired = false;
window.addEventListener('scroll', (e) => {
const scrollable = document.documentElement.scrollHeight - window.innerHeight; // Numbers of pixels the user can scroll
const scrolled = window.scrollY; // Number of pixels the user has scrolled
if (fired === false) {
fired = true;
// If the user scrolls down the scrolled value increases and vice versa
// So basically
if (scrolled < lastScrolled) {
// Then the user scrolled up
console.log('GET OUT! YOU SCROLLED UP!');
// But you should update the lastScrolled value nonetheless
lastScrolled = scrolled;
setTimeout(() => {
// not the best kind of solution
fired = false;
}, 500);
return; // And then get out!
}
// Before we continue
// It gets weird when it reaches the bottom of page so i had to add this fix
// Checks if the user scrolled all the way to the bottom of page and returns
if (Math.ceil(scrollable) === scrolled) {
console.log('STOP!');
fired = false;
return;
}
// And finally you need a setTimeOut function for this to work
// Because you need to add and remove the "scroll-animation" class between some kind of time interval
// Add class as soon as the scroll event starts
container.classList.add('scroll-animation');
// And remove it after the time interval
setTimeout(() => {
container.classList.remove('scroll-animation');
fired = false;
console.log('Scrolling has stopped.');
}, 1050);
}
lastScrolled = scrolled; // The value gets updated in any case
});
Codepen
For some reason div.container goes crazy if you don't specify height
grid-gap: 0.1rem property seems to cause the problem.
html, body {
height: 100%;
}
.container {
height: 100%;
...rest...
}
Also grid-column: span minmax(2, 3); doesnt work.

Change css :onhover distance

I was wondering if there was a way to make css :onhover occur sooner (i.e. I'm 10px away from a link, and :onhover calls, while still 10px away).
Edit: I'm trying to figure out a way to change the :onhover distance for a scrollbar.
Edit 2: Here's the code:
html {
overflow: auto;
}
body {
position: absolute;
top: 20px;
left: 20px;
bottom: 20px;
right: 20px;
padding: 30px;
overflow-y: scroll;
overflow-x: hidden;
}
::-webkit-scrollbar {
width: 5px;
}
/* Track */
::-webkit-scrollbar-track {
-webkit-border-radius: 10px;
border-radius: 10px;
border: none;
}
/* Handle */
::-webkit-scrollbar-thumb{
-webkit-border-radius: 10px;
border-radius: 10px;
background: rgba(200,200,200,0.1);
}
::-webkit-scrollbar-thumb:hover {
-webkit-border-radius: 10px;
border-radius: 10px;
background: rgba(200,200,200,0.8);
}
Add CSS border property to the element which then will receive the hover event.
Reference 1: jsFiddle Proximity Detector
Edit: I now see you've re-written a major edit to your Question.
I've looked at your code and have come up with a method that you might like for Chrome's scrollbar!
Reference 2: jsFiddle Scrollbar Proximity Detector
I find the CSS methods for solving this intrusive. Transparent <divs>, extra markup, and margin/padding methods can get in the way of surrounding UI. But those would be the pure CSS methods to try if they work for you.
If you're open to using Javascript to solve this, you can capture mousemove on the body and calculate the relation.
Demo: http://jsfiddle.net/ThinkingStiff/Buaze/
Script
function isNear( element, distance, event ) {
var left = element.documentOffsetLeft - distance,
top = element.documentOffsetTop - distance,
right = left + element.clientWidth + ( 2 * distance ),
bottom = top + element.clientHeight + ( 2 * distance ),
x = event.pageX,
y = event.pageY;
return ( x > left && x < right && y > top && y < bottom );
};
document.body.addEventListener( 'mousemove', function () {
var near = document.getElementById( 'near' );
if( isNear( near, 20, event ) ) {
near.textContent = 'is near!';
} else {
near.textContent = '';
};
} );
window.Object.defineProperty( Element.prototype, 'documentOffsetTop', {
get: function () {
return this.offsetTop + ( this.offsetParent ? this.offsetParent.documentOffsetTop : 0 );
}
} );
window.Object.defineProperty( Element.prototype, 'documentOffsetLeft', {
get: function () {
return this.offsetLeft + ( this.offsetParent ? this.offsetParent.documentOffsetLeft : 0 );
}
} );
HTML
<div id="near"></div>
CSS
#near {
border: 1px solid red;
height: 100px;
left: 50px;
line-height: 100px;
position: relative;
text-align: center;
top: 50px;
width: 100px;
}
Nope, but you could position a transparent div that's 10 pixels larger than your target element directly over it, then give that an onhover function with Javascript. I don't think this is possible with pure CSS, though.
You don't need to use javascript, you could set a parent wrapping container with padding:10px and set the :hover selector on that instead of your child element.

Prevent body scrolling but allow overlay scrolling

I've been searching for a "lightbox" type solution that allows this but haven't found one yet (please, suggest if you know of any).
The behavior I'm trying to recreate is just like what you'd see at Pinterest when clicking on an image. The overlay is scrollable (as in the whole overlay moves up like a page on top of a page) but the body behind the overlay is fixed.
I attempted to create this with just CSS (i.e. a div overlay on top of the whole page and body with overflow: hidden), but it doesn't prevent div from being scrollable.
How to keep the body/page from scrolling but keep scrolling inside the fullscreen container?
Theory
Looking at current implementation of the pinterest site (it might change in the future), when you open the overlay, a noscroll class is applied to the body element (setting overflow: hidden) making the body no longer scrollable.
The overlay created on-the-fly or already injected in the page and made visible via display: block — it makes no difference – has position : fixed and overflow-y: scroll, with top, left, right and bottom properties set to 0: this style makes the overlay fill the whole viewport (but now we are in 2022, so you may use inset: 0 instead).
The div inside the overlay is in position: static so the vertical scrollbar is related to that element. This is resulting in a scrollable but fixed overlay.
When you close the overlay, you have to hide it (using display: none) and you could even remove the node via javascript (or just the content inside, it's up to you but also depends on the nature of the content).
The final step is to also remove the noscroll class applied to the body (so the overflow property gets back to the value it had previously)
Code
Codepen Example
(it works by changing the aria-hidden attribute of the overlay in order to show and hide it and to increase its accessibility).
Markup
(open button)
<button type="button" class="open-overlay">OPEN LAYER</button>
(overlay and close button)
<section class="overlay" aria-hidden="true" tabindex="-1">
<div>
<h2>Hello, I'm the overlayer</h2>
...
<button type="button" class="close-overlay">CLOSE LAYER</button>
</div>
</section>
CSS
.noscroll {
overflow: hidden;
}
.overlay {
position: fixed;
overflow-y: scroll;
inset: 0; }
[aria-hidden="true"] { display: none; }
[aria-hidden="false"] { display: block; }
Javascript (vanilla-JS)
var body = document.body,
overlay = document.querySelector('.overlay'),
overlayBtts = document.querySelectorAll('button[class$="overlay"]'),
openingBtt;
[].forEach.call(overlayBtts, function(btt) {
btt.addEventListener('click', function() {
/* Detect the button class name */
var overlayOpen = this.className === 'open-overlay';
/* storing a reference to the opening button */
if (overlayOpen) {
openingBtt = this;
}
/* Toggle the aria-hidden state on the overlay and the
no-scroll class on the body */
overlay.setAttribute('aria-hidden', !overlayOpen);
body.classList.toggle('noscroll', overlayOpen);
/* On some mobile browser when the overlay was previously
opened and scrolled, if you open it again it doesn't
reset its scrollTop property */
overlay.scrollTop = 0;
/* forcing focus for Assistive technologies but note:
- if your modal has just a phrase and a button move the
focus on the button
- if your modal has a long text inside (e.g. a privacy
policy) move the focus on the first heading inside
the modal
- otherwise just focus the modal.
When you close the overlay restore the focus on the
button that opened the modal.
*/
if (overlayOpen) {
overlay.focus();
}
else {
openingBtt.focus();
openingBtt = null;
}
}, false);
});
/* detect Escape key when the overlay is open */
document.body.addEventListener('keyup', (ev) => {
if (ev.key === "Escape" && overlay.getAttribute('aria-hidden') === 'false') {
overlay.setAttribute('aria-hidden', 'true');
body.classList.toggle('noscroll', false);
openingBtt.focus();
openingBtt = null;
}
})
Finally, here's another example in which the overlay opens with a fade-in effect by a CSS transition applied to the opacity property. Also a padding-right is applied to avoid a reflow on the underlying text when the scrollbar disappears.
Codepen Example (fade)
CSS
.noscroll { overflow: hidden; }
#media (min-device-width: 1025px) {
/* not strictly necessary, just an experiment for
this specific example and couldn't be necessary
at all on some browser */
.noscroll {
padding-right: 15px;
}
}
.overlay {
position: fixed;
overflow-y: scroll;
inset: 0;
}
[aria-hidden="true"] {
transition: opacity 1s, z-index 0s 1s;
width: 100vw;
z-index: -1;
opacity: 0;
}
[aria-hidden="false"] {
transition: opacity 1s;
width: 100%;
z-index: 1;
opacity: 1;
}
overscroll-behavior css property allows to override the browser's default overflow scroll behavior when reaching the top/bottom of content.
Just add the following styles to overlay:
.overlay {
overscroll-behavior: contain;
...
}
Codepen demo
Currently works in Chrome, Firefox and IE(caniuse)
For more details check google developers article.
If you want to prevent overscrolling on ios, you can add position fixed to your .noscroll class
body.noscroll{
position:fixed;
overflow:hidden;
}
Most solutions have the problem that they do not retain the scroll position, so I took a look at how Facebook does it. In addition to setting the underlaying content to position: fixed they also set the top dynamically to retain the scroll position:
scrollPosition = window.pageYOffset;
mainEl.style.top = -scrollPosition + 'px';
Then, when you remove the overlay again, you need to reset the scroll position:
window.scrollTo(0, scrollPosition);
I created a little example to demonstrate this solution
let overlayShown = false;
let scrollPosition = 0;
document.querySelector('.toggle').addEventListener('click', function() {
if (!overlayShown) {
showOverlay();
} else {
removeOverlay();
}
overlayShown = !overlayShown;
});
function showOverlay() {
scrollPosition = window.pageYOffset;
const mainEl = document.querySelector('.main-content');
mainEl.style.top = -scrollPosition + 'px';
document.body.classList.add('show-overlay');
}
function removeOverlay() {
document.body.classList.remove('show-overlay');
window.scrollTo(0, scrollPosition);
const mainEl = document.querySelector('.main-content');
mainEl.style.top = 0;
}
.main-content {
background-image: repeating-linear-gradient( lime, blue 103px);
width: 100%;
height: 200vh;
}
.show-overlay .main-content {
position: fixed;
left: 0;
right: 0;
overflow-y: scroll; /* render disabled scroll bar to keep the same width */
/* Suggestion to put: overflow-y: hidden;
Disabled scrolling still makes a mess with its width. Hiding it does the trick. */
}
.overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.3);
overflow: auto;
}
.show-overlay .overlay {
display: block;
}
.overlay-content {
margin: 50px;
background-image: repeating-linear-gradient( grey, grey 20px, black 20px, black 40px);
height: 120vh;
}
.toggle {
position: fixed;
top: 5px;
left: 15px;
padding: 10px;
background: red;
}
/* reset CSS */
body {
margin: 0;
}
<main class="main-content"></main>
<div class="overlay">
<div class="overlay-content"></div>
</div>
<button class="toggle">Overlay</button>
Don't use overflow: hidden; on body. It automatically scrolls everything to the top. There's no need for JavaScript either. Make use of overflow: auto;. This solution even works with mobile Safari:
HTML Structure
<div class="overlay">
<div class="overlay-content"></div>
</div>
<div class="background-content">
lengthy content here
</div>
Styling
.overlay{
position: fixed;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
background-color: rgba(0, 0, 0, 0.8);
.overlay-content {
height: 100%;
overflow: scroll;
}
}
.background-content{
height: 100%;
overflow: auto;
}
See the demo here and source code here.
Update:
For people who want keyboard space bar, page up/down to work: you need to focus on the overlay, e.g., clicking on it, or manually JS focusing on it before this part of the div will respond to keyboard. Same with when the overlay is "switched off", since it's just moving the overlay to the side. Otherwise to browser, these are just two normal divs and it wouldn't know why it should focus on any one of them.
It is worth noting that sometimes adding "overflow:hidden" to the body tag doesn't do the job. In those cases, you'll have to add the property to the html tag as well.
html, body {
overflow: hidden;
}
The behaviour you want to prevent is called scroll chaining. To disable it, set
overscroll-behavior: contain;
on your overlay in CSS.
You can easily do this with some "new" css and JQuery.
Initially: body {... overflow:auto;}
With JQuery you can dynamically switch between 'overlay' and 'body'. When on 'body', use
body {
position: static;
overflow: auto;
}
When on 'overlay' use
body {
position: sticky;
overflow: hidden;
}
JQuery for the switch('body'->'overlay'):
$("body").css({"position": "sticky", "overflow": "hidden"});
JQuery for the switch('overlay'->'body'):
$("body").css({"position": "static", "overflow": "auto"});
if anyone is looking for a solution for React function components, you can put this inside the modal component:
useEffect(() => {
document.body.style.overflowY = 'hidden';
return () =>{
document.body.style.overflowY = 'auto';
}
}, [])
Generally speaking, if you want a parent (the body in this case) to prevent it from scrolling when a child (the overlay in this case) scrolls, then make the child a sibling of the parent to prevent the scroll event from bubbling up to the parent. In case of the parent being the body, this requires an additional wrapping element:
<div id="content">
</div>
<div id="overlay">
</div>
See Scroll particular DIV contents with browser's main scrollbar to see its working.
The chosen answer is correct, but has some limitations:
Super hard "flings" with your finger will still scroll <body> in the background
Opening the virtual keyboard by tapping an <input> in the modal will direct all future scrolls to <body>
I don't have a fix for the first issue, but wanted to shed some light on the second. Confusingly, Bootstrap used to have the keyboard issue documented, but they claimed it was fixed, citing http://output.jsbin.com/cacido/quiet as an example of the fix.
Indeed, that example works fine on iOS with my tests. However, upgrading it to the latest Bootstrap (v4) breaks it.
In an attempt to figure out what the difference between them was, I reduced a test case to no longer depend on Bootstrap, http://codepen.io/WestonThayer/pen/bgZxBG.
The deciding factors are bizarre. Avoiding the keyboard issue seems to require that background-color is not set on the root <div> containing the modal and the modal's content must be nested in another <div>, which can have background-color set.
To test it, uncomment the below line in the Codepen example:
.modal {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 2;
display: none;
overflow: hidden;
-webkit-overflow-scrolling: touch;
/* UNCOMMENT TO BREAK */
/* background-color: white; */
}
For touch devices, try adding a 1px wide, 101vh min-height transparent div in the wrapper of the overlay. Then add -webkit-overflow-scrolling:touch; overflow-y: auto; to the wrapper. This tricks mobile safari into thinking the overlay is scrollable, thus intercepting the touch event from the body.
Here's a sample page. Open on mobile safari: http://www.originalfunction.com/overlay.html
https://gist.github.com/YarGnawh/90e0647f21b5fa78d2f678909673507f
I found this question trying to solve issue I had with my page on Ipad and Iphone - body was scrolling when I was displaying fixed div as popup with image.
Some answers are good, however none of them solved my issue. I found following blog post by Christoffer Pettersson. Solution presented there helped issue I had with iOS devices and it helped my scrolling background problem.
Six things I learnt about iOS Safari's rubber band scrolling
As it was suggested I include major points of the blog post in case link gets outdated.
"In order to disable that the user can scroll the background page while the "menu is open", it is possible to control what elements should be allowed to be scrolled or not, by applying some JavaScript and a CSS class.
Based on this Stackoverflow answer you can control that elements with the disable-scrolling should not
perform their default scroll action when the touchmove event is triggered."
document.ontouchmove = function ( event ) {
var isTouchMoveAllowed = true, target = event.target;
while ( target !== null ) {
if ( target.classList && target.classList.contains( 'disable-scrolling' ) ) {
isTouchMoveAllowed = false;
break;
}
target = target.parentNode;
}
if ( !isTouchMoveAllowed ) {
event.preventDefault();
}
};
And then put the disable-scrolling class on the page div:
<div class="page disable-scrolling">
Simple inline styling for the body tag:
<body style="position: sticky; overflow: hidden;">
If the intent is to disable on mobile/ touch devices then the most straightforward way to do it is using touch-action: none;.
Example:
const app = document.getElementById('app');
const overlay = document.getElementById('overlay');
let body = '';
for (let index = 0; index < 500; index++) {
body += index + '<br />';
}
app.innerHTML = body;
app.scrollTop = 200;
overlay.innerHTML = body;
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
}
#app {
background: #f00;
position: absolute;
height: 100%;
width: 100%;
overflow-y: scroll;
line-height: 20px;
}
#overlay {
background: rgba(0,0,0,.5);
position: fixed;
top: 0;
left: 0;
right: 0;
height: 100%;
padding: 0 0 0 100px;
overflow: scroll;
}
<div id='app'></div>
<div id='overlay'></div>
(The example does not work in the context of Stack Overflow. You will need to recreate it in a stand-alone page.)
If you want to disable scrolling of the #app container, just add touch-action: none;.
I'd like to add to previous answers because I tried to do that, and some layout broke as soon as I switched the body to position:fixed. In order to avoid that, I had to also set body's height to 100% :
function onMouseOverOverlay(over){
document.getElementsByTagName("body")[0].style.overflowY = (over?"hidden":"scroll");
document.getElementsByTagName("html")[0].style.position = (over?"fixed":"static");
document.getElementsByTagName("html")[0].style.height = (over?"100%":"auto");
}
Use the following HTML:
<body>
<div class="page">Page content here</div>
<div class="overlay"></div>
</body>
Then JavaScript to intercept and stop scrolling:
$(".page").on("touchmove", function(event) {
event.preventDefault()
});
Then to get things back to normal:
$(".page").off("touchmove");
In my case, none of these solutions worked out on iPhone (iOS 11.0).
The only effective fix that is working on all my devices is this one - ios-10-safari-prevent-scrolling-behind-a-fixed-overlay-and-maintain-scroll-position
try this
var mywindow = $('body'), navbarCollap = $('.navbar-collapse');
navbarCollap.on('show.bs.collapse', function(x) {
mywindow.css({visibility: 'hidden'});
$('body').attr("scroll","no").attr("style", "overflow: hidden");
});
navbarCollap.on('hide.bs.collapse', function(x) {
mywindow.css({visibility: 'visible'});
$('body').attr("scroll","yes").attr("style", "");
});
One solution for a React functional component is to use the useEffect hook.
Here's the code example bellow (pay attention to the useEffect definition):
import {useEffect, useRef} from "react";
export default function PopoverMenu({className, handleClose, children}) {
const selfRef = useRef(undefined);
useEffect(() => {
const isPopoverOpenned = selfRef.current?.style.display !== "none";
const focusedElement = document?.activeElement;
const scrollPosition = {x: window.scrollX, y: window.scrollY};
if (isPopoverOpenned) {
preventDocBodyScrolling();
} else {
restoreDocBodyScrolling();
}
function preventDocBodyScrolling() {
const width = document.body.clientWidth;
const hasVerticalScrollBar = (window.innerWidth > document.documentElement.clientWidth);
document.body.style.overflowX = "hidden";
document.body.style.overflowY = hasVerticalScrollBar ? "scroll" : "";
document.body.style.width = `${width}px`;
document.body.style.position = "fixed";
}
function restoreDocBodyScrolling() {
document.body.style.overflowX = "";
document.body.style.overflowY = "";
document.body.style.width = "";
document.body.style.position = "";
focusedElement?.focus();
window.scrollTo(scrollPosition.x, scrollPosition.y);
}
return () => {
restoreDocBodyScrolling(); // cleanup on unmount
};
}, []);
return (
<>
<div
className="backdrop"
onClick={() => handleClose && handleClose()}
/>
<div
className={`pop-over-menu${className ? (` ${className}`) : ""}`}
ref={selfRef}
>
<button
className="pop-over-menu--close-button" type="button"
onClick={() => handleClose && handleClose()}
>
X
</button>
{children}
</div>
</>
);
}
Originally posted on this other related Stackoverflow question: https://stackoverflow.com/a/69016517/14131330
CSS
.noScroll {
overflow: hidden;
}
Javascript
<script>
function toggleNav() {
document.body.classList.toggle("noScroll");
}
</script>
Button
<button onclick="toggleNav()">
Toggle Nav
</button>
If you want to stop body/html scroll add as the following
CSS
html, body {
height: 100%;
}
.overlay{
position: fixed;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
background-color: rgba(0, 0, 0, 0.8);
.overlay-content {
height: 100%;
overflow: scroll;
}
}
.background-content{
height: 100%;
overflow: auto;
}
HTML
<div class="overlay">
<div class="overlay-content"></div>
</div>
<div class="background-content">
lengthy content here
</div>
Basically, you could do it without JS.
The main idea is to add html/body with height: 100% and overflow: auto.
and inside your overlay, you could either enable/disable scroll based on your requirement.
Hope this helps!
Use below code for disabling and enabling scroll bar.
Scroll = (
function(){
var x,y;
function hndlr(){
window.scrollTo(x,y);
//return;
}
return {
disable : function(x1,y1){
x = x1;
y = y1;
if(window.addEventListener){
window.addEventListener("scroll",hndlr);
}
else{
window.attachEvent("onscroll", hndlr);
}
},
enable: function(){
if(window.removeEventListener){
window.removeEventListener("scroll",hndlr);
}
else{
window.detachEvent("onscroll", hndlr);
}
}
}
})();
//for disabled scroll bar.
Scroll.disable(0,document.body.scrollTop);
//for enabled scroll bar.
Scroll.enable();

Resources