Hiding Vertical Scrollbars with Pure CSS in Chrome, IE (6+), Firefox, Opera, and Safari

I just spent the past day trying to figure out the best way to hide scrollbars using pure CSS in the latest versions of every browser.  I wanted to be able to still scroll within the element, however, so using:

 .element { overflow: hidden; }

 was not an option.  I looked at how Facebook hides their scrollbars in the chat window, I looked at recommendations made on StackOverflow, and I invented a lot of solutions on my own.  Eventually, I arrived at a solution that I like.

 

So first, a little background... my preliminary research revealed the following:

  • There is a CSS rule that can hide scrollbars in Webkit-based browsers (Chrome and Safari).  That rule is: 

     .element::-webkit-scrollbar { width: 0 !important }
    
  • There is a CSS rule that can hide scrollbars in IE 10+.  That rule is: 

     .element { -ms-overflow-style: none; }
    
  • There used to be a CSS rule that could hide scrollbars in Firefox, but it has since been deprecated.  That rule was: 

     .element { overflow: -moz-scrollbars-none; }
    

 

The StackOverflow-recommended approach was to give my element scrollbars, wrap it in a

containing element, and make the containing element element smaller, and give it:

 overflow: hidden;

 

This is the approach Facebook uses for their chat window. However, there are two problems with

this approach:

  1. We have to hard-code in the width of a scrollbar, but this width can vary from browser to browser
  2. In Webkit browsers like Chrome and Safari, if we highlight text and drag to the right, it scrolls the inner element to the left and the scrollbars are revealed

See this screenshot from Facebook, after I clicked and dragged to the right:

 

I searched StackOverflow to see if there were any fixes to the clicking and dragging issue.  I found a JavaScript solution that seemed to work, but it was kind of hacky.  Essentially, you bind to the scroll event for your element and set the scrollLeft to 0 every time the scroll event fires.  That seems to work, but if the browser lags at all, it can look glitchy.  I continued searching for a better solution.

One solution I found recommended setting:

 .element:active { pointer-events: none; }

That prevented Webkit users from scrolling to the right when clicking and dragging to the right on text, but it also prevented them from scrolling down when clicking and dragging down on the text.  Clearly that wouldn't be a good solution.

 

Then I thought: what if instead of covering the scrollbars with a containing element, I used an ::after element to just put a white box overtop of the scrollbar?  That seemed to work, but it required that I knew how wide the scrollbar was.  After a little tweaking, I found a way to create an element that was just as wide as a scrollbar and covered the existing scrollbar.  Essentially, what I did was create an element that was nothing but a vertical scrollbar.  Then I put an ::after element on that that covered it with a solid color.  Finally, I moved the whole piece overtop of the existing scrollbar.  This method was pretty simple, but it still left a lot to be desired.  What if I didn't want the extra white space to the right of my scrollable element?  I continued searching for a better solution...

 

Next, I tried to see if there would be some hacky way to hide scrollbars in Firefox.  I could already hide them in Chrome, Safari, and IE, so Firefox was the last remaining puzzle piece.  I stumbled upon the -moz-appearance CSS property and tried a number of options.  One thing that almost worked was:

 .element { -moz-appearance: menuimage; }

This removed the scrollbars from the element, but unfortunately, it also restricted the width and height of my element.  I couldn't find any way to change the size.  After many hours, I gave up on hiding scrollbars in Firefox.

 

Then another idea came to mind.  What if the used the transform property of CSS to mess with the scrollbars somehow?  The transform property affects everything in the element, including the scrollbars!  I decided that the scale option would be my best bet.  What I ended up doing was scaling the x axis of the scrollbar element by a factor of about 0.001.  This made the scrollbar so thin that it disappeared.  Then, I wrapped the text content in another element and scaled the x axis of that element by a factor of 1000.  That seemed to work!  However, text highlighting was glitchy in Firefox with this approach.

 

I then thought of another idea that involved using transform.  What if I mirrored the element with the scrollbar, and then mirrored the text inside?  That would put the scrollbar on the left instead of the right.  I did that, and then I gave the element with the scrollbar a negative margin of 17px (the width of my scrollbar).  The scrollbar disappeared since it went outside the region of the containing element (which had overflow: hidden;).  As an added bonus, when I highlighted text in Webkit and dragged to the left, the scrollbar didn't scroll into view!  Unfortunately, this solution required me to know exactly how wide the scrollbar was.  I was looking for a solution that didn't require me to hardcode the width of the scrollbar.

 

Then, I found that I could set position: absolute; and right: 0; and it would move the scrollbar out of view without having to know the width.  That solution worked well in all browsers but IE.  For some reason, when the scrollbar gets reversed, IE removes the margin between the text and the scrollbar.  What ended up happening was that the left side of the text was getting chopped off.  So then I added a Webkit-specific rule to reverse the scrollbars only when the user was using a Webkit browser.  This solution worked in all browsers and can be seen here.

 

But then I got to thinking... wait a minute, why not just hide the scrollbar for Webkit browsers and use the overflow: hidden; solution for all other browsers?  But of course!  How simple!   I did make some changes to the original overflow: hidden; solution, however.  I set the desired width on both the element that contains the text and the outermost container.  Then I set position: absolute; and left: 0; on the text element so that the scrollbar would be out of view.  This approach doesn't need to know the width of the scrollbar ahead of time in order to work.  That solution works for all browsers too, and can be seen here.

 

And here is the full code:

HTML

 <div class="outer-container">
 <div class="inner-container">
 <div class="element">
 Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 Integer vehicula quam nibh, eu tristique tellus dignissim
 quis. Integer condimentum ultrices elit ut mattis.
 Praesent rhoncus tortor metus, nec pellentesque enim
 mattis nec. Nulla vitae turpis ut dui consectetur
 pellentesque quis vel est. Curabitur rutrum, mauris ut
 mollis lobortis, sem est congue lectus, ut sodales nunc
 leo a libero. Cras quis sapien in mi fringilla tempus
 condimentum quis velit. Aliquam id aliquam arcu. Morbi
 tristique aliquam rutrum. Duis tincidunt, orci suscipit
 cursus molestie, purus nisi pharetra dui, tempor
 dignissim felis turpis in mi. Vivamus ullamcorper arcu
 sit amet mauris egestas egestas. Vestibulum turpis neque,
 condimentum a tincidunt quis, molestie vel justo. Sed
 molestie nunc dapibus arcu feugiat, ut sollicitudin metus
 sagittis. Aliquam a volutpat sem. Quisque id magna
 ultrices, lobortis dui eget, pretium libero. Curabitur
 aliquam in ante eu ultricies.
 
 Quisque vitae tincidunt purus. Vivamus feugiat bibendum
 erat, nec interdum urna porta sed. Nunc lobortis neque
 orci, ut suscipit nisl congue feugiat. Vivamus feugiat
 tellus quis cursus sollicitudin. Curabitur dolor massa,
 dictum ut ipsum in, porttitor pellentesque ante. Aenean
 egestas cursus tempor. Maecenas semper tortor sit amet
 egestas cursus. Mauris porttitor quis nisi ut tincidunt.
 Curabitur adipiscing eleifend nibh. Praesent mauris leo,
 consequat vitae orci eget, vestibulum bibendum nisi.
 Aliquam tempus diam ut tortor cursus, eget sodales augue
 adipiscing. Nulla at dignissim libero.
 </div>
 </div>
 </div>

 

CSS

 .element, .outer-container {
 width: 200px;
 height: 200px;
}
 
.outer-container {
 border: 5px solid purple;
 position: relative;
 overflow: hidden;
}
 
.inner-container {
 position: absolute;
 left: 0;
 overflow-x: hidden;
 overflow-y: scroll;
}
 
.inner-container::-webkit-scrollbar {
 display: none;
}
 

 

I hope this helps someone!  Enjoy :)