How to prevent scrollbar from repositioning web page?

HtmlCss

Html Problem Overview


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?

Html Solutions


Solution 1 - Html

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;
}

Solution 2 - Html

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.

Solution 3 - Html

I think not. But styling body with overflow: scroll should do. You seem to know that, though.

Solution 4 - Html

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.

Solution 5 - Html

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][1] for some explanation)
calc calculate in css3
34px stands for double scrollbar width (see [this][2] for fixed or [this to calculate][3] if you don't trust fixed sizes)

[1]: http://css-tricks.com/viewport-sized-typography/ "Viewport Sized Typography" [2]: http://www.textfixer.com/tutorials/browser-scrollbar-width.php "Width of a Browser Scrollbar" [3]: http://jdsharp.us/jQuery/minute/calculate-scrollbar-width.php "Calculating The Browser Scrollbar Width"

Solution 6 - Html

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;
}

Solution 7 - Html

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

Solution 8 - Html

@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);
  }
});

Solution 9 - Html

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.

Solution 10 - Html

Summary

I see three ways - each with their own quirks:

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.

Solution 11 - Html

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";

Solution 12 - Html

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%);
}

Solution 13 - Html

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

Solution 14 - Html

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>

Solution 15 - 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.

Solution 16 - Html

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! :)

Solution 17 - Html

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à!

Solution 18 - Html

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.

Solution 19 - Html

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;
}

Solution 20 - Html

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

Solution 21 - Html

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.

Solution 22 - Html

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.

Solution 23 - Html

I used some jquery to solve this

$('html').css({
    'overflow-y': 'hidden'
});

$(document).ready(function(){
  $(window).load(function() {
    $('html').css({
      'overflow-y': ''
    });
  });
});

Solution 24 - Html

@media screen and (min-width: 1024px){
	body {
	min-height: 700px
	}
}

Solution 25 - Html

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDmitri NesterukView Question on Stackoverflow
Solution 1 - HtmlRubenView Answer on Stackoverflow
Solution 2 - HtmlRaptiView Answer on Stackoverflow
Solution 3 - HtmlMichael Krelin - hackerView Answer on Stackoverflow
Solution 4 - HtmlpongoView Answer on Stackoverflow
Solution 5 - HtmlMoesioView Answer on Stackoverflow
Solution 6 - HtmlkunalkambleView Answer on Stackoverflow
Solution 7 - Htmluser185710View Answer on Stackoverflow
Solution 8 - HtmlHASSAN MD TAREQView Answer on Stackoverflow
Solution 9 - HtmlBarsonaxView Answer on Stackoverflow
Solution 10 - HtmlBjarke PjedstedView Answer on Stackoverflow
Solution 11 - HtmlFrank LView Answer on Stackoverflow
Solution 12 - HtmlGrant GryczanView Answer on Stackoverflow
Solution 13 - HtmlGreg St.OngeView Answer on Stackoverflow
Solution 14 - HtmlRobert James MietaView Answer on Stackoverflow
Solution 15 - HtmlkashesandrView Answer on Stackoverflow
Solution 16 - HtmlMelvin GuerreroView Answer on Stackoverflow
Solution 17 - HtmlJonathan SIView Answer on Stackoverflow
Solution 18 - HtmlMaciej ManistaView Answer on Stackoverflow
Solution 19 - HtmlL LawlietView Answer on Stackoverflow
Solution 20 - HtmlMarkus T.View Answer on Stackoverflow
Solution 21 - HtmlJames GunawanView Answer on Stackoverflow
Solution 22 - Htmluser3697030View Answer on Stackoverflow
Solution 23 - HtmlWill McMillianView Answer on Stackoverflow
Solution 24 - HtmlVickyView Answer on Stackoverflow
Solution 25 - HtmlNikunj MadhogariaView Answer on Stackoverflow