HTML reserve space for scrollbar - html

I have a div that may overflow as content is added or removed.
However the UI designer does not want a visible, but inactive scrollbar (as with overflow: scroll), and they don't want the content layout to change when one is added and remove (as with overflow: auto).
Is there a means to get this behavior, and considering the different scrollbars on different platforms and browsers.
https://jsfiddle.net/qy9a2r00/1/

No browser support this property yet (2021), but scrollbar-gutter is a suggested solution for this.
Update: 2022 - all modern browsers except Safari support the property.

The only way to do this is to make the width of the items in the container fixed. And you'll have to be conservative with the width of the scrollbar.
.container {
width: 200px;
height: 200px;
overflow: auto;
overflow-x: hidden;
border: 1px solid black;
}
.item {
width: 200px;
...
https://jsfiddle.net/fr1jnmn6/1/

overflow-y:overlay would be a partial solution to this as in, it solves the problem of not wanting the content layout to change when a scrollbar is added or removed. Extra padding or margin can be added so that scrollbar doesn't obfuscate the div behind
Here's the jsfiddle for the same https://jsfiddle.net/kitwradr/2qcsj6hw/

One cannot know how thick the scrollbar is, using only HTML & CSS and thus do not know the width of the (blue) placeholder.
You might solve such a task using scripting. Force a scrollbar in a hidden container and measure the inner and outer width. The difference being the scrollbar-width. Set this width (e.g. as CSS) to the placeholder element. And in the tricky part hide this element whenever a scrollbar is shown.
The usual solution to this problem is the one you do not want.

You can setup overflow: scroll to reserve space for scrollbar and add a class that makes scrollbar hidden
body {
overflow-y: scroll;
}
Hide the scrollbar by adding class to div (or to body) that will make your scrollbar transparent
.scroll-hidden::-webkit-scrollbar
{
background-color: transparent;
}
.scroll-hidden::-webkit-scrollbar-track
{
background-color: transparent;
}
To check if you have content overflow you can use this lines:
const { body } = document
const overflow = body.scrollHeight > body.clientHeight
If there are no overflow issue we will hide scrollbar and with reserve space
body.classList.add('scroll-hidden')
If content overflow we will show scrollbar
body.classList.remove('scroll-hidden')
Try the solution here https://jsfiddle.net/ycharniauski/y0pwftmq/
It's a bit hacky solution. Hope in future css will have some property to reserve space

You need to have a parent div with a fixed width (the final total width) and a child div with a width 16px smaller.
Then the scrollbar will have 16px free in the parent div.
The width should always be a number (can't be relative value). In the child div, you need to use a number as well. You can't do calc(100%-16px).
<div className="user-list">
<div className="user-list__content">
{content}
</div>
</div>
.user-list {
width: 500px; /* total width */
height: 1000x;
overflow-y: auto;
}
.user-list__content {
width: 484px; /* must be 16px smaller */
height: 100%;
}

This is an ancient question, but in case anyone comes looking.
Detect the scrollbar and show/hide your blue section based on the scrollbar being visible. If scrollbar is visible, HIDE your blue sections (apply a style). If not visible, show your blue padding section.
When the scrollbar becomes visible your padding hides, so the red and green sections will not change size or position.
This article below discusses detecting the scrollbar. You will want to set up a div somewhere to detect the current scrollbar width ahead of time in order to set your blue boxes to the same width.
How can I check if a scrollbar is visible?

Maybe append a div at the bottom will soft your problem ?
https://jsfiddle.net/moongod101/k7w574mw/1/

Related

Pure css way to prevent scrollbar pushing content to the left?

Is there a way to prevent scrollbar from pushing content, or the entire page to the left with pure css?
I mean no hacks or anything.
I tried two javascript solutions:
1) Set body to overflow hidden, store the body.offsetWidth in a variable, then overflow visible and then subtract that offsetWidth with the current body.offsetWidth and apply the difference to the right margin.
2) Calculate the offsetWidth and apply it on the wrapper div on every resize.
What didnt work:
1) Position absolute.
2) Floating everything to the left was a bad idea.
3) Leaving the scrollbar visible (Looks bad).
4) Overflow-y hidden makes things user unfriendly.
There are a lot of ways to go around this issue though normally you won't mind a little push to the left:
Give overflow-y: scroll to body and make sure always there is a scrollbar.
Make use of the fact that viewport width includes the scrollbar while percentages do not account for it:
a. Giving width: 100vw to body element, or
b. Giving margin-left: calc(100vw - 100%) to the html element so that scrollbar or not, you have a fixed area to work on.
There is even a deprecated overflow: overlay property that draws over the page instead of shifting it to the left.
Just give your body a width of 100vw like this:
body{
width: 100vw;
}
Even though all the answers above are correct, I stumbled upon this issue and I had to come up with another solution.
Since my content width takes up the whole page and it has some properties to justify in the center, it was being pushed to the left and these options didn't prevent it from happening.
What fixed the problem for me was to add a padding of the size of the scroll when the scroll is added on hover.
I tested on Chrome and Edge. It's not a perfect fix but it is enough for what I need right now.
.scrollable {
width: 100%;
height: 91vh;
overflow: hidden;
padding: 0px !important;
}
.scrollable:hover {
width: 100%;
height: 91vh;
overflow-y: auto;
padding-left: 16.8px !important;
}
Unfortunately there is no other way to prevent your problem.
Just use
body {
overflow:hidden;
}
As an alternative, I recommend you to use a Framework for custom scroll bars. Or disable the scrollbar as shown in the above snippet and emulate it with an absolute positioned and some JS.
Of course you will have to consider calculating the height of the page and the offset of the scrollbar thumb.
I hope that helps.
To disable the horizontal scrollbar, you can use overflow-x, so it won't affect your vertical scroll:
body {
overflow-x: hidden;
}
Just set overflow-x to hidden on the element that has the scrollbar (usually this would be the body or the immediate children of it).
I had the same problem on my nextjs app which already had overflow-x set to hidden on the body. The below solution worked for me
#__next{
overflow-x: hidden;
}

page shifts when the height exceeds the browser size [duplicate]

I have a website with center-aligned DIV. Now, some pages need scrolling, some don't. When I move from one type to another, the appearance of a scrollbar moves the page a few pixels to the side. Is there any way to avoid this without explicitly showing the scrollbars on each page?
overflow-y:scroll is correct, but you should use it with the html tag, not body or else you get a double scrollbar in IE 7
So the correct css would be:
html {
overflow-y: scroll;
}
Wrap the content of your scrollable element into a div and apply padding-left: calc(100vw - 100%);.
<body>
<div style="padding-left: calc(100vw - 100%);">
Some Content that is higher than the user's screen
</div>
</body>
The trick is that 100vw represents 100% of the viewport including the scrollbar. If you subtract 100%, which is the available space without the scrollbar, you end up with the width of the scrollbar or 0 if it is not present. Creating a padding of that width on the left will simulate a second scrollbar, shifting centered content back to the right.
Please note that this will only work if the scrollable element uses the page's entire width, but this should be no problem most of the time because there are only few other cases where you have centered scrollable content.
html {
overflow-x: hidden;
margin-right: calc(-1 * (100vw - 100%));
}
Example. Click "change min-height" button.
With calc(100vw - 100%) we can calculate the width of the scrollbar (and if it is not displayed, it will be 0). Idea: using negative margin-right, we can increase the width of <html> to this width. You will see a horizontal scroll bar — it should be hidden using overflow-x: hidden.
I think not. But styling body with overflow: scroll should do. You seem to know that, though.
With scroll always being shown, maybe be not good for layout.
Try to limit body width with css3
body {
width: calc(100vw - 34px);
}
vw is the width of the viewport (see this link for some explanation)
calc calculate in css3
34px stands for double scrollbar width (see this for fixed or this to calculate if you don't trust fixed sizes)
If changing size or after loading some data it is adding the scroll bar then you can try following, create class and apply this class.
.auto-scroll {
overflow-y: overlay;
overflow-x: overlay;
}
I don't know if this is an old post, but i had the same problem and if you want to scroll vertically only you should try overflow-y:scroll
body {
scrollbar-gutter: stable both-edges;
}
New css spec that will help with scrollbar repositioning is on its way:
https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-gutter
Summary
I see three ways - each with their own quirks:
scrollbar-gutter as mentioned by Markus T.
overflow: overlay as mentioned by kunalkamble
Add spacing with calc(100vw - 100%) as mentioned Rapti
Here is a StackBlitz demo
Press the "Toggle height" to see the content shift.
scrollbar-gutter
This has limited support but with a #support media query we can use a combination of this and overflow-y: scroll:
html {
overflow-y: scroll;
}
#supports (scrollbar-gutter: stable) {
html {
overflow-y: auto;
scrollbar-gutter: stable;
}
}
In this way content will never shift.
The "problem" with this solution is that there is always a fixed space for the scrollbar.
overflow: overlay
Limited support and it obviously hides anything it overlays. Special care is needed to make sure nothing vital is hidden (also on zoom and text size changes).
Can be combined with scrollbar-gutter:
html {
overflow-y: scroll;
}
#supports (scrollbar-gutter: stable) {
html {
overflow-y: auto;
scrollbar-gutter: stable;
}
}
#supports (overflow-y: overlay) {
html {
overflow-y: overlay;
scrollbar-gutter: auto;
}
}
It is possible to do some negative margin and overflow-x: hidden but this has a risk of hiding vital content under certain situations. Small screen, custom font/zoom size, browser extensions, etc.
calc(100vw - 100%)
This can be done with RTL support like this:
html[dir='ltr'] main {
padding-left: calc(100vw - 100%);
}
html[dir='rtl'] main {
padding-right: calc(100vw - 100%);
}
Where <main> in this case would be the container for the centered content.
Content here will not shift as long as the centered container is smaller than <main>. But as soon as it is 100% of the container a padding will be introduced. See the StackBlitz demo and click "Toggle width".
The "problem" with this solution is that you need media queries to prevent padding on "small screens" and that even on small screens - when the scrollbar should be visible - some shifting will occur because there is no room for 100% content and a scrollbar.
Conclusion
Use scrollbar-gutter perhaps combined with overlay. If you absolutely don't want empty spacing, try the calc solution with media queries.
Simply setting the width of your container element like this will do the trick
width: 100vw;
This will make that element ignore the scrollbar and it works with background color or images.
#kashesandr's solution worked for me but to hide horizontal scrollbar I added one more style for body. here is complete solution:
CSS
<style>
/* prevent layout shifting and hide horizontal scroll */
html {
width: 100vw;
}
body {
overflow-x: hidden;
}
</style>
JS
$(function(){
/**
* For multiple modals.
* Enables scrolling of 1st modal when 2nd modal is closed.
*/
$('.modal').on('hidden.bs.modal', function (event) {
if ($('.modal:visible').length) {
$('body').addClass('modal-open');
}
});
});
JS Only Solution (when 2nd modal opened from 1st modal):
/**
* For multiple modals.
* Enables scrolling of 1st modal when 2nd modal is closed.
*/
$('.modal').on('hidden.bs.modal', function (event) {
if ($('.modal:visible').length) {
$('body').addClass('modal-open');
$('body').css('padding-right', 17);
}
});
I've solved the issue on one of my websites by explicitly setting the width of the body in javascript by the viewport size minus the width of the scrollbar. I use a jQuery based function documented here to determine the width of the scrollbar.
<body id="bodyid>
var bodyid = document.getElementById('bodyid');
bodyid.style.width = window.innerWidth - scrollbarWidth() + "px";
Extending off of Rapti's answer, this should work just as well, but it adds more margin to the right side of the body and hides it with negative html margin, instead of adding extra padding that could potentially affect the page's layout. This way, nothing is changed on the actual page (in most cases), and the code is still functional.
html {
margin-right: calc(100% - 100vw);
}
body {
margin-right: calc(100vw - 100%);
}
Expanding on the answer using this:
body {
width: calc(100vw - 17px);
}
One commentor suggested adding left-padding as well to maintain the centering:
body {
padding-left: 17px;
width: calc(100vw - 17px);
}
But then things don't look correct if your content is wider than the viewport. To fix that, you can use media queries, like this:
#media screen and (min-width: 1058px) {
body {
padding-left: 17px;
width: calc(100vw - 17px);
}
}
Where the 1058px = content width + 17 * 2
This lets a horizontal scrollbar handle the x overflow and keeps the centered content centered when the viewport is wide enough to contain your fixed-width content
If the width of the table won't change, you can set the width of the element (such as tbody) that contains the scrollbar > 100% (allowing extra space for the scrollbar) and set overflow-y to "overlay" (so that the scrollbar stays fixed, and won't shift the table left when it appears). Also set a fixed height for the element with the scrollbar, so the scrollbar will appear once the height is exceeded. Like so:
tbody {
height: 100px;
overflow-y: overlay;
width: 105%
}
Note: you will have to manually adjust the width % as the % of space the scrollbar takes up will be relative to your table width (ie: smaller width of table, more % required to fit the scrollbar, as it's size in pixels is constant)
A dynamic table example:
function addRow(tableID)
{
var table = document.getElementById(tableID);
var rowCount = table.rows.length;
var row = table.insertRow(rowCount);
var colCount = table.rows[0].cells.length;
for(var i=0; i<colCount; i++)
{
var newRow = row.insertCell(i);
newRow.innerHTML = table.rows[0].cells[i].innerHTML;
newRow.childNodes[0].value = "";
}
}
function deleteRow(row)
{
var table = document.getElementById("data");
var rowCount = table.rows.length;
var rowIndex = row.parentNode.parentNode.rowIndex;
document.getElementById("data").deleteRow(rowIndex);
}
.scroll-table {
border-collapse: collapse;
}
.scroll-table tbody {
display:block;
overflow-y:overlay;
height:60px;
width: 105%
}
.scroll-table tbody td {
color: #333;
padding: 10px;
text-shadow: 1px 1px 1px #fff;
}
.scroll-table thead tr {
display:block;
}
.scroll-table td {
border-top: thin solid;
border-bottom: thin solid;
}
.scroll-table td:first-child {
border-left: thin solid;
}
.scroll-table td:last-child {
border-right: thin solid;
}
.scroll-table tr:first-child {
display: none;
}
.delete_button {
background-color: red;
color: white;
}
.container {
display: inline-block;
}
body {
text-align: center;
}
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="test_table.css">
</head>
<body>
<h1>Dynamic Table</h1>
<div class="container">
<table id="data" class="scroll-table">
<tbody>
<tr>
<td><input type="text" /></td>
<td><input type="text" /></td>
<td><input type="button" class="delete_button" value="X" onclick="deleteRow(this)"></td>
</tr>
</tbody>
</table>
<input type="button" value="Add" onclick="addRow('data')" />
</div>
<script src="test_table.js"></script>
</body>
</html>
I tried to fix likely the same issue which caused by twitter bootstrap .modal-open class applied to body. The solution html {overflow-y: scroll} doesn't help. One possible solution I found is to add {width: 100%; width: 100vw} to the html element.
I use to have that problem, but the simples way to fix it is this (this works for me):
on the CSS file type:
body{overflow-y:scroll;}
as that simple! :)
The solutions posted using calc(100vw - 100%) are on the right track, but there is a problem with this: You'll forever have a margin to the left the size of the scrollbar, even if you resize the window so that the content fills up the entire viewport.
If you try to get around this with a media query you'll have an awkward snapping moment because the margin won't progressively get smaller as you resize the window.
Here's a solution that gets around that and AFAIK has no drawbacks:
Instead of using margin: auto to center your content, use this:
body {
margin-left: calc(50vw - 500px);
}
Replace 500px with half the max-width of your content (so in this example the content max-width is 1000px). The content will now stay centered and the margin will progressively decrease all the way until the content fills the viewport.
In order to stop the margin from going negative when the viewport is smaller than the max-width just add a media query like so:
#media screen and (max-width:1000px) {
body {
margin-left: 0;
}
}
Et voilà!
After trying most of the above CSS or JS-based solutions that haven't worked in my case, just wanted to add up to it.
My solution worked for the case where the scrollbar had to disappear on an event (e.g. a button click, cause you've just opened a full-screen menu that should block the page from being scrollable).
This should work when the below styles are applied to the element that turns overflow-y to hidden (in my case it's the body tag):
body {
overflow-x: hidden;
width: 100vw;
margin-right: calc(100vw - 100%);
}
Explanation: The width of your body tag is 100vw (so it includes the scrollbar's width).
By setting the margin-right, the margin only gets applied if your vertical scrollbar is visible (so your page content isn't actually under the scrollbar), meaning the page content will not reposition once overflow-y has changed.
Note: this solution only works for the pages that are not horizontally-scrollable.
Tested on Chrome 89.0, Firefox 87.0, Safari 14.0.3
Update: unfortunately it only works with centered container that doesn't take 100% width - otherwise the scrollbar overlays the piece of content on the right.
My approach is to make the track transparent. The scroll bar thumb color is #C1C1C1 to match the default scrollbar thumb color. You can make it anything you prefer :)
Try this:
html {
overflow-y: scroll;
}
body::-webkit-scrollbar {
width: 0.7em;
background-color: transparent;
}
body::-webkit-scrollbar-thumb {
background: #C1C1C1;
height:30px;
}
body::-webkit-scrollbar-track-piece
{
display:none;
}
I know the question is very old, but there is a new better method.
scrollbar-gutter: stable;
I tried overflow scroll but it didn't work for my case. the scroll bar still adds some kind of (white) padding. what works is changing the width from 100vw to 100%, but for the height it is ok to use 100vh. so this:
const Wrapper = styled.div`
min-height: 100vh
`
const Parent = styled.div`
width: 100%
`
const Children = styled.div`
width: 100%
`
Edit
I've set the width twice because the parent component held a sidebar, and the children. Depending on your use case, you can set it once.
Since I haven't found my solution here I would like to add it:
I did not want a permanent scrollbar (accepted solution) and I also decided to not use negative margins. They didn't (instantly) work for me in chrome and I also did not want to have content possibly disappearing below the scrollbar.
So this is a padding solution.
My web page consists of three parts:
Header (content is left aligned)
MainContent (content is centered)
Footer (content is left and right aligned)
Since the header would look bad with a left padding and since the logo should stay in the corner of the page, I kept it unchanged since the appearing of a scrollbar does not affect it in most cases (except when window width is very small).
Since an even padding is acceptable for both the MainContent and the footer I used only for those both containers the following css:
.main-content, .footer {
/*
* Set the padding to the maximum scrollbar width minus the actual scrollbar width.
* Maximum scrollbar width is 17px according to: https://codepen.io/sambible/post/browser-scrollbar-widths
*/
padding-right: calc(17px - (100vw - 100%));
padding-left: 17px;
}
This will keep the MainContent in the exact center and also work for all scrollbar width up to 17px. One could add a media query removing these paddings for mobile devices that have an overlay scrollbar.
This solution is similar to only adding the left padding and setting the width to "width: calc(100vw - 17px);". I cannot say if it would behave equally in all cases though.
I used some jquery to solve this
$('html').css({
'overflow-y': 'hidden'
});
$(document).ready(function(){
$(window).load(function() {
$('html').css({
'overflow-y': ''
});
});
});
#media screen and (min-width: 1024px){
body {
min-height: 700px
}
}
Contrary to the accepted answer which suggests a permanent scroll bar on the browser window even if the content doesn't overflow the screen, I would prefer using:
html{
height:101%;
}
This is because the appearance of scroll bar makes more sense if the content actually overflows.
This makes more sense than this.

How can I get things properly contained in a wrapper div?

At cjshayward.com/index_new.html, there is a wrapper div around the body's content, about 1000 pixels wide, and it works as intended for the top 100 or so pixels in Chrome and Firefox. Next down the page is a jQuery UI set of tabs, containing a fixed-width accordion and something close to jQuery.load()ed plain old, simple HTML.
However, on the "Browse the Library" tab (but not "About the Author"), which is presently open and which contains the fixed-width accordion, below 100 or 150px down, the area under the tabs appears to have the same width as the window; it has the correct left margin, and horizontally scrolls an apparently equal distance to the right. Furthermore, the body background tile does not display; the whole width is white, as was specified for the wrapper div's interior.
How can I get the "Browse the Library" tab to display as intended (like the "About the Author" tab does)?
Thanks,
You're absolutely positioning way too much and that's ruining the flow of things. I'll go through a list of edits you can do to make this work.
/*
#accordion and #details will be floated, so we'll need to
clear #tabs. Add this property.
*/
#tabs {
overflow: hidden;
}
/*
Remove the absolute positioning from #accordion, along
with the top and left properties and do this instead.
*/
#accordion {
float: left;
width: 400px; /* This already exists */
margin: 0 10px 0 0;
}
/*
Remove the absolute positioning from #details, along
with the top and left properties and do this instead.
*/
#details {
float: left;
width: 580px;
}
This will get you a lot closer. You should also try to avoid using height on these elements. Let the content dictate the height.
Here is what i ended up with making those edits: http://i.imgur.com/niizuoR.png
Okay lets make a step by step solution (watch for the edits).
Background
Your background is set in the body. So the body needs to be extended to fill the whole page.
I would recommend this way but there are others.
body,html{
height:100%;
}
Normally the body would fit its contents but with position:absolute this mechanism doesnt work anymore.
Also remove background: #fff css (normalize.css) from the html.
html {
background: #fff;
color: #000;
font-size: 100%;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
Also your background scrolls with your content. Set background-atachment: fixed to change this.
Wrapper
Same counts dor your wrapper which holds the white background.
Set its height to 100% too.
div#main {
height: 100%;
}
The reason why your content is bigger than your wrapper is that
<div id="details" style="width: 713px; height: 0px;">
this div holding the content has a fixed size set. Removing that size make it fit the wrapper.
The width seems to be set per javascript in the load event, so I cant help you with that. Provide your .js code and may i can help you with that too.
As stated in the comments, your layout issues are based in your use of absolute positioning rather than flow layout:
I went through your site and quickly switch everything so it was positioned statically (width floats, not absolute values) and this cleared up the issue. There were some other issues as well. You probably need to look over how you are setting up your HTML from the top level on.
I would start out again and concentrate on using floats for your layout, rather than absolute positioning.
For a basic example on doing so, here is a super simply page: http://cdpn.io/kmCFy

How to prevent horizontal scrolling on responsive webpage?

I'm using twitter bootstrap to make my app responsive. When I shrink the width of my browser window to the minimum size or view the page on a mobile device (iPhone, example), the user is able to scroll horizontally. The amount of horizontal scroll is small but I would like to remove it all together.
I believe it's due to the img container that I'm including, but I'm not here. The page's html is located here: http://pastebin.ca/2347946.
Any advice on how to prevent the horizontal scroll for the page while still maintaining the scrolling in the img container?
I had the same problem, and applied this to fix it:
body {
overflow-x: hidden !important;
}
.container {
max-width: 100% !important;
overflow-x: hidden !important;
}
To expand a bit, I only had the problem on mobile phones, so this is my final code:
#media screen and (max-width: 667px) {
body {
overflow-x: hidden !important;
}
.container {
max-width: 100% !important;
overflow-x: hidden !important;
}
}
I found the issues regarding this on Github, so I guess newer versions of Bootstrap have been patched.
i am pretty sure somewhere one of your child element is exceeding the width of its parent element. Check you code twice, if there any box-size of inner child elements is large because of one of the reasons like- when the box width including margin, padding, border go beyond the limit. And we possibly haven't added overflow: hidden; to its outer parent element. In this case they are considered reaching beyond their parent element and browsers show it with the scrollbar. So fix it via removing extra margins, paddings, borders or if you just don't want any headache, just try to add overflow:hidden; to the outer box.
The "overflow: hidden;" style is the remedy not the prevention.
If you want to prevent this you have to check your body elements which is larger than the body.
It happens when you take a div with some "padding" or "margin" outside ".container" class (twitter bootstrap).
So remove all padding and margin outside the container class.
It turns out I had zoomed in more than 100% that was causing the page to scroll. Cmd+0 helped bring the zoom to 100% which got rid of the scrolling.
Try to add (in the relevant #-rule) a max-width:
img {
max-width: NNNpx;
}
That'll prevent img from being too wide.

How to prevent scrollbar from repositioning web page?

I have a website with center-aligned DIV. Now, some pages need scrolling, some don't. When I move from one type to another, the appearance of a scrollbar moves the page a few pixels to the side. Is there any way to avoid this without explicitly showing the scrollbars on each page?
overflow-y:scroll is correct, but you should use it with the html tag, not body or else you get a double scrollbar in IE 7
So the correct css would be:
html {
overflow-y: scroll;
}
Wrap the content of your scrollable element into a div and apply padding-left: calc(100vw - 100%);.
<body>
<div style="padding-left: calc(100vw - 100%);">
Some Content that is higher than the user's screen
</div>
</body>
The trick is that 100vw represents 100% of the viewport including the scrollbar. If you subtract 100%, which is the available space without the scrollbar, you end up with the width of the scrollbar or 0 if it is not present. Creating a padding of that width on the left will simulate a second scrollbar, shifting centered content back to the right.
Please note that this will only work if the scrollable element uses the page's entire width, but this should be no problem most of the time because there are only few other cases where you have centered scrollable content.
html {
overflow-x: hidden;
margin-right: calc(-1 * (100vw - 100%));
}
Example. Click "change min-height" button.
With calc(100vw - 100%) we can calculate the width of the scrollbar (and if it is not displayed, it will be 0). Idea: using negative margin-right, we can increase the width of <html> to this width. You will see a horizontal scroll bar — it should be hidden using overflow-x: hidden.
I think not. But styling body with overflow: scroll should do. You seem to know that, though.
With scroll always being shown, maybe be not good for layout.
Try to limit body width with css3
body {
width: calc(100vw - 34px);
}
vw is the width of the viewport (see this link for some explanation)
calc calculate in css3
34px stands for double scrollbar width (see this for fixed or this to calculate if you don't trust fixed sizes)
If changing size or after loading some data it is adding the scroll bar then you can try following, create class and apply this class.
.auto-scroll {
overflow-y: overlay;
overflow-x: overlay;
}
I don't know if this is an old post, but i had the same problem and if you want to scroll vertically only you should try overflow-y:scroll
body {
scrollbar-gutter: stable both-edges;
}
New css spec that will help with scrollbar repositioning is on its way:
https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-gutter
Summary
I see three ways - each with their own quirks:
scrollbar-gutter as mentioned by Markus T.
overflow: overlay as mentioned by kunalkamble
Add spacing with calc(100vw - 100%) as mentioned Rapti
Here is a StackBlitz demo
Press the "Toggle height" to see the content shift.
scrollbar-gutter
This has limited support but with a #support media query we can use a combination of this and overflow-y: scroll:
html {
overflow-y: scroll;
}
#supports (scrollbar-gutter: stable) {
html {
overflow-y: auto;
scrollbar-gutter: stable;
}
}
In this way content will never shift.
The "problem" with this solution is that there is always a fixed space for the scrollbar.
overflow: overlay
Limited support and it obviously hides anything it overlays. Special care is needed to make sure nothing vital is hidden (also on zoom and text size changes).
Can be combined with scrollbar-gutter:
html {
overflow-y: scroll;
}
#supports (scrollbar-gutter: stable) {
html {
overflow-y: auto;
scrollbar-gutter: stable;
}
}
#supports (overflow-y: overlay) {
html {
overflow-y: overlay;
scrollbar-gutter: auto;
}
}
It is possible to do some negative margin and overflow-x: hidden but this has a risk of hiding vital content under certain situations. Small screen, custom font/zoom size, browser extensions, etc.
calc(100vw - 100%)
This can be done with RTL support like this:
html[dir='ltr'] main {
padding-left: calc(100vw - 100%);
}
html[dir='rtl'] main {
padding-right: calc(100vw - 100%);
}
Where <main> in this case would be the container for the centered content.
Content here will not shift as long as the centered container is smaller than <main>. But as soon as it is 100% of the container a padding will be introduced. See the StackBlitz demo and click "Toggle width".
The "problem" with this solution is that you need media queries to prevent padding on "small screens" and that even on small screens - when the scrollbar should be visible - some shifting will occur because there is no room for 100% content and a scrollbar.
Conclusion
Use scrollbar-gutter perhaps combined with overlay. If you absolutely don't want empty spacing, try the calc solution with media queries.
Simply setting the width of your container element like this will do the trick
width: 100vw;
This will make that element ignore the scrollbar and it works with background color or images.
#kashesandr's solution worked for me but to hide horizontal scrollbar I added one more style for body. here is complete solution:
CSS
<style>
/* prevent layout shifting and hide horizontal scroll */
html {
width: 100vw;
}
body {
overflow-x: hidden;
}
</style>
JS
$(function(){
/**
* For multiple modals.
* Enables scrolling of 1st modal when 2nd modal is closed.
*/
$('.modal').on('hidden.bs.modal', function (event) {
if ($('.modal:visible').length) {
$('body').addClass('modal-open');
}
});
});
JS Only Solution (when 2nd modal opened from 1st modal):
/**
* For multiple modals.
* Enables scrolling of 1st modal when 2nd modal is closed.
*/
$('.modal').on('hidden.bs.modal', function (event) {
if ($('.modal:visible').length) {
$('body').addClass('modal-open');
$('body').css('padding-right', 17);
}
});
I've solved the issue on one of my websites by explicitly setting the width of the body in javascript by the viewport size minus the width of the scrollbar. I use a jQuery based function documented here to determine the width of the scrollbar.
<body id="bodyid>
var bodyid = document.getElementById('bodyid');
bodyid.style.width = window.innerWidth - scrollbarWidth() + "px";
Extending off of Rapti's answer, this should work just as well, but it adds more margin to the right side of the body and hides it with negative html margin, instead of adding extra padding that could potentially affect the page's layout. This way, nothing is changed on the actual page (in most cases), and the code is still functional.
html {
margin-right: calc(100% - 100vw);
}
body {
margin-right: calc(100vw - 100%);
}
Expanding on the answer using this:
body {
width: calc(100vw - 17px);
}
One commentor suggested adding left-padding as well to maintain the centering:
body {
padding-left: 17px;
width: calc(100vw - 17px);
}
But then things don't look correct if your content is wider than the viewport. To fix that, you can use media queries, like this:
#media screen and (min-width: 1058px) {
body {
padding-left: 17px;
width: calc(100vw - 17px);
}
}
Where the 1058px = content width + 17 * 2
This lets a horizontal scrollbar handle the x overflow and keeps the centered content centered when the viewport is wide enough to contain your fixed-width content
If the width of the table won't change, you can set the width of the element (such as tbody) that contains the scrollbar > 100% (allowing extra space for the scrollbar) and set overflow-y to "overlay" (so that the scrollbar stays fixed, and won't shift the table left when it appears). Also set a fixed height for the element with the scrollbar, so the scrollbar will appear once the height is exceeded. Like so:
tbody {
height: 100px;
overflow-y: overlay;
width: 105%
}
Note: you will have to manually adjust the width % as the % of space the scrollbar takes up will be relative to your table width (ie: smaller width of table, more % required to fit the scrollbar, as it's size in pixels is constant)
A dynamic table example:
function addRow(tableID)
{
var table = document.getElementById(tableID);
var rowCount = table.rows.length;
var row = table.insertRow(rowCount);
var colCount = table.rows[0].cells.length;
for(var i=0; i<colCount; i++)
{
var newRow = row.insertCell(i);
newRow.innerHTML = table.rows[0].cells[i].innerHTML;
newRow.childNodes[0].value = "";
}
}
function deleteRow(row)
{
var table = document.getElementById("data");
var rowCount = table.rows.length;
var rowIndex = row.parentNode.parentNode.rowIndex;
document.getElementById("data").deleteRow(rowIndex);
}
.scroll-table {
border-collapse: collapse;
}
.scroll-table tbody {
display:block;
overflow-y:overlay;
height:60px;
width: 105%
}
.scroll-table tbody td {
color: #333;
padding: 10px;
text-shadow: 1px 1px 1px #fff;
}
.scroll-table thead tr {
display:block;
}
.scroll-table td {
border-top: thin solid;
border-bottom: thin solid;
}
.scroll-table td:first-child {
border-left: thin solid;
}
.scroll-table td:last-child {
border-right: thin solid;
}
.scroll-table tr:first-child {
display: none;
}
.delete_button {
background-color: red;
color: white;
}
.container {
display: inline-block;
}
body {
text-align: center;
}
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="test_table.css">
</head>
<body>
<h1>Dynamic Table</h1>
<div class="container">
<table id="data" class="scroll-table">
<tbody>
<tr>
<td><input type="text" /></td>
<td><input type="text" /></td>
<td><input type="button" class="delete_button" value="X" onclick="deleteRow(this)"></td>
</tr>
</tbody>
</table>
<input type="button" value="Add" onclick="addRow('data')" />
</div>
<script src="test_table.js"></script>
</body>
</html>
I tried to fix likely the same issue which caused by twitter bootstrap .modal-open class applied to body. The solution html {overflow-y: scroll} doesn't help. One possible solution I found is to add {width: 100%; width: 100vw} to the html element.
I use to have that problem, but the simples way to fix it is this (this works for me):
on the CSS file type:
body{overflow-y:scroll;}
as that simple! :)
The solutions posted using calc(100vw - 100%) are on the right track, but there is a problem with this: You'll forever have a margin to the left the size of the scrollbar, even if you resize the window so that the content fills up the entire viewport.
If you try to get around this with a media query you'll have an awkward snapping moment because the margin won't progressively get smaller as you resize the window.
Here's a solution that gets around that and AFAIK has no drawbacks:
Instead of using margin: auto to center your content, use this:
body {
margin-left: calc(50vw - 500px);
}
Replace 500px with half the max-width of your content (so in this example the content max-width is 1000px). The content will now stay centered and the margin will progressively decrease all the way until the content fills the viewport.
In order to stop the margin from going negative when the viewport is smaller than the max-width just add a media query like so:
#media screen and (max-width:1000px) {
body {
margin-left: 0;
}
}
Et voilà!
After trying most of the above CSS or JS-based solutions that haven't worked in my case, just wanted to add up to it.
My solution worked for the case where the scrollbar had to disappear on an event (e.g. a button click, cause you've just opened a full-screen menu that should block the page from being scrollable).
This should work when the below styles are applied to the element that turns overflow-y to hidden (in my case it's the body tag):
body {
overflow-x: hidden;
width: 100vw;
margin-right: calc(100vw - 100%);
}
Explanation: The width of your body tag is 100vw (so it includes the scrollbar's width).
By setting the margin-right, the margin only gets applied if your vertical scrollbar is visible (so your page content isn't actually under the scrollbar), meaning the page content will not reposition once overflow-y has changed.
Note: this solution only works for the pages that are not horizontally-scrollable.
Tested on Chrome 89.0, Firefox 87.0, Safari 14.0.3
Update: unfortunately it only works with centered container that doesn't take 100% width - otherwise the scrollbar overlays the piece of content on the right.
My approach is to make the track transparent. The scroll bar thumb color is #C1C1C1 to match the default scrollbar thumb color. You can make it anything you prefer :)
Try this:
html {
overflow-y: scroll;
}
body::-webkit-scrollbar {
width: 0.7em;
background-color: transparent;
}
body::-webkit-scrollbar-thumb {
background: #C1C1C1;
height:30px;
}
body::-webkit-scrollbar-track-piece
{
display:none;
}
I know the question is very old, but there is a new better method.
scrollbar-gutter: stable;
I tried overflow scroll but it didn't work for my case. the scroll bar still adds some kind of (white) padding. what works is changing the width from 100vw to 100%, but for the height it is ok to use 100vh. so this:
const Wrapper = styled.div`
min-height: 100vh
`
const Parent = styled.div`
width: 100%
`
const Children = styled.div`
width: 100%
`
Edit
I've set the width twice because the parent component held a sidebar, and the children. Depending on your use case, you can set it once.
Since I haven't found my solution here I would like to add it:
I did not want a permanent scrollbar (accepted solution) and I also decided to not use negative margins. They didn't (instantly) work for me in chrome and I also did not want to have content possibly disappearing below the scrollbar.
So this is a padding solution.
My web page consists of three parts:
Header (content is left aligned)
MainContent (content is centered)
Footer (content is left and right aligned)
Since the header would look bad with a left padding and since the logo should stay in the corner of the page, I kept it unchanged since the appearing of a scrollbar does not affect it in most cases (except when window width is very small).
Since an even padding is acceptable for both the MainContent and the footer I used only for those both containers the following css:
.main-content, .footer {
/*
* Set the padding to the maximum scrollbar width minus the actual scrollbar width.
* Maximum scrollbar width is 17px according to: https://codepen.io/sambible/post/browser-scrollbar-widths
*/
padding-right: calc(17px - (100vw - 100%));
padding-left: 17px;
}
This will keep the MainContent in the exact center and also work for all scrollbar width up to 17px. One could add a media query removing these paddings for mobile devices that have an overlay scrollbar.
This solution is similar to only adding the left padding and setting the width to "width: calc(100vw - 17px);". I cannot say if it would behave equally in all cases though.
I used some jquery to solve this
$('html').css({
'overflow-y': 'hidden'
});
$(document).ready(function(){
$(window).load(function() {
$('html').css({
'overflow-y': ''
});
});
});
#media screen and (min-width: 1024px){
body {
min-height: 700px
}
}
Contrary to the accepted answer which suggests a permanent scroll bar on the browser window even if the content doesn't overflow the screen, I would prefer using:
html{
height:101%;
}
This is because the appearance of scroll bar makes more sense if the content actually overflows.
This makes more sense than this.