How to detect on which side of the browser is scrollbar - right or left (in case of RTL)?
Asked Answered
R

6

28

For <html dir="rtl"> some browsers (Safari, Edge, IE) will automatically move the scrollbar to the left side which is the correct behavior:

enter image description here

Unfortunately, major browsers (Chrome and Firefox) are behaving in a different way, the scrollbar is still on the right side of the browser.

Is it possible to detect programmatically (preferably with vanilla JS) on which side is scrollbar?


UPD (28.09.2018): so far, there's no working solution in the answers, people are trying to detect the scrollbar position by using getBoundingClientRect().left or .offsetLeft and that will not work because it will always be 0 at least in Edge independently from the scrollbar position.


UPD (15.10.2018): TLDR: there's no way.

It seems that browsers do not provide API for detection the scrollbar side. In answers below there are tricks with insertion of a dummy div and calculating where is scrollbar in that div. I can not consider those tricks as an answer because the document scrollbar could be positioned with multiple ways (<html dir="rtl">, RTL version of OS, browser settings, etc.)

Refined answered 19/9, 2018 at 10:57 Comment(9)
Tip: in Firefox it's possible to force scrollbar to be on the left side by changing layout.scrollbar.side to 3 in about:configRefined
Please take a look at github.com/shadiabuhilal/rtl-detect It looks line there is no easy way to detect. Hopefully, it will give some idea.Havoc
thanks for your response @Havoc but that library isn't really what I'm looking for.Refined
You can detect what language is on your website using that library, and then set something like this *{direction: rtl}Havoc
I'm trying to understand the problem you are facing. Curious to know what do you need it for?Wallaroo
Just thinking out loud, but wouldn't it be easier to detect the browser (from the User Agent) and map it to it's scrollbar behavior (right/left) based on that? Something like a simple hashmap: {chrome: 'right', firefox: 'right', edge: 'left', safari: 'left', ie: 'left'}Wallaroo
@GabrielC.Troia > I'm trying to understand the problem you are facing. Curious to know what do you need it for? github.com/sweetalert2/sweetalert2/issues/1221Refined
You need the attribute dir necessarily on the html tag, or on the body tag could be ok if it places the scrollbar always on the left (in case of rtl) ?Bedivere
Se mu innovative answer. maybe it could help you.Irrespirable
B
4

As it is shown here, the scrollbar won't change side with dir="rtl" on html or body HTML tag in Firefox, Chrome, Opera & Safari. It works on IE & Edge. I tried (on Edge 17, IE 11, Firefox 62, Chrome 69, Opera 56, Safari 5.1) and it is still relevant in october 2018 (Safari > 9.1 display scrollbar on left side if dir="rtl").

I didn't find any cross browser native solution until now to find the position of the scrollbar as you asked. I don't think you have one. We can only try to find "hack" to fix it...

Based on your question / comments and on your real issue here, i think it would be better to focus on the compatibility of dir="rtl". You are trying to find out the position of the scrollbar because it was not working as expected in the first place. To make it work as expected, a quick fix could be to put the scroll on the body element :

<!DOCTYPE html>
<html dir="rtl">
  <head>
    <meta charset="utf-8">
    <title></title>
    <style>
      html, body {
        height: 100%;
        overflow: hidden;
      }

      body {
        padding: 0;
        margin: 0;
        overflow-y: auto; /* or overflow: auto; */
      }

      p {
        margin: 0;
        font-size: 8em;
      }
    </style>
  </head>

  <body>

    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure
      dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>

  </body>
</html>

This css code will make the body element to scroll instead of the html element. Strangly, put the scroll on body will display the scrollbar at the right place. It works for recent versions of browsers.

The exact compatibility is (oldest working version tested) :

  • IE => v6 - 2001
  • Edge => all
  • Firefox => v4 - 2011
  • Opera => v10 - 2009
  • Chrome => v19 - 2012
  • Safari => v10.1 - 2016

With the browsers which are compatible, you can "trust" the scrollbar position based on the dir attribute. It means that for browsers listed above, the scrollbar will be on the left for dir="rtl" and on the right for dir="ltr". I tested and it works.

For older browsers versions, i don't have fix for the moment. It means that you won't be fully compatible, but it's mainly a problem with safari as you can see.

EDIT : Research to find the position of the scrollbar

As i mentionned above, i think we can try to find a "hack" to find the position of the scrollbar. I am not a css expert so my solution is not very pretty. Someone with css skills could probably find a good solution.

If the scroll is on the body (solution above), we can detect the scrollbar position with css positioned element. For example this code :

<!DOCTYPE html>
<html dir="rtl">
<head>
  <meta charset="utf-8">
  <title></title>
  <style>
    html, body {
      height: 100%;
      overflow: hidden;
    }

    body {
      padding: 0;
      margin: 0;
      overflow-y: auto;
    }

    p {
      margin: 0;
      font-size: 8em;
    }

    #sc { position: relative; float: left; }
    #sc_2 { position: relative; float: right; }
    #sc_3 { position: absolute; width: 100%; }
  </style>
</head>
<body>
  <div id="sc"></div>
  <div id="sc_2"></div>
  <div id="sc_3"></div>
  
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
  tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
  quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
  consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
  cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
  proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>

  <script>
    var html = document.getElementsByTagName('html')[0];
    var sc = document.getElementById('sc');
    var sc_2 = document.getElementById('sc_2');
    var sc_3 = document.getElementById('sc_3');

    if (sc_2.offsetLeft < html.clientWidth && !(sc_3.offsetLeft < 0)) {
      console.log('bar on the right');
    } else if (sc.offsetLeft > 0 || sc_3.offsetLeft < 0) {
      console.log('bar on the left');
    } else {
      console.log('no bar');
    }
  </script>
</body>
</html>

The compatibility is (oldest working version tested):

  • IE => v6 - 2001
  • Edge => all
  • Firefox => v4 - 2011
  • Opera => v10 - 2009
  • Chrome => v15 - 2011
  • Safari => not working

This solution is not very useful if the scroll is on the body tag because as mentionned above in this case we can "trust" the dir attribute. I put it here because it could probably be adapted for the scroll on the html tag.

Bedivere answered 11/10, 2018 at 12:24 Comment(0)
R
2

I improved on Andre's answer:

All you need is getComputedStyle()

console.clear();

const btn = document.querySelector('Button');
const html = document.querySelector('html');
const wrap = document.querySelector('#myBodyWrapper');
const input = document.querySelector('input');

var state = 'ltr';
var inner = '';

for (var i = 0; i < 500; i++) {
  inner += 'Lorem ipsum Bla bla bla<br>';
}
wrap.innerHTML = inner;

btn.addEventListener('click', function() {
  toggleDirection(html)
}, false);


function getDirection(elem) {
  return window.getComputedStyle(elem).direction
}
function setDirection(elem, direction) {
  elem.setAttribute('dir', direction)
}
function toggleDirection(elem) {
  if (getDirection(elem) === 'ltr' || getDirection(elem) === undefined) {
    setDirection(elem, 'rtl')
  } else {
    setDirection(elem, 'ltr')
  }
  input.value = getDirection(elem)
}

input.value = getDirection(html)
body {
  margin: 0;
  padding: 0;
  overflow: hidden;
  text-align: center;
}

div#myBodyWrapper {
  overflow: auto;
  height: 80vh;
  text-align: start;
  margin: 10px 40px;
  padding: 10px;
  background-color: goldenrod;
}
button {
  background-color:gold;
  padding:5px;
  margin:5px 0;
}

input {
  background-color: silver;
  text-align: center;
  padding: 5px;
  font-size: 20px;
}
<button>Switch scrollbar</button><br>
<input type="text" value="">
<div id="myBodyWrapper"></div>
Russophobe answered 10/10, 2018 at 10:28 Comment(1)
window.getComputedStyle won't work. The position of browser's scrollbar can be determined not by website only. It can be set system-wide (e.g. Arabic Windows) or in browser settings (e.g. Firefox's layout.scrollbar.side set to 3)Refined
E
0

It's not a very robust solution but it can be used as a basis to create a better one.

Basic logic is that child elements shift to right when there's a scrollbar on the left side of container element. All we have to do is measure that shift in presence and absence of scrollbar.

Here's a simple function based on jQuery which does that. scrollElement is the element where we have to detect scrollbar position. Mostly it should be html/body but not necessarily, I don't know of any easy methods to find that out.

function isScrollLeft(scrollElement) {
    var $scrollElement = $(scrollElement);
    $scrollElement.append('<div id="scroll_rtl_tester" style="width=100%;height: 1px;"/>');

    var $scrollTester = $('#scroll_rtl_tester');
    var beforeLeftOffset = $scrollTester.offset().left;

    var originalOverflow = $scrollElement.css('overflow-y');
    $scrollElement.css('overflow-y', 'hidden');

    var afterLeftOffset = $scrollTester.offset().left;
    $scrollElement.css('overflow-y', originalOverflow);

    $scrollTester.remove();

    var isRtl = false;

    if(beforeLeftOffset > afterLeftOffset) {
        isRtl = true;
    }

    return isRtl;
}
Eclecticism answered 21/9, 2018 at 23:41 Comment(1)
Sorry @Eclecticism but this is not cross-browser solution, not working in Microsoft Edge and IE, proof: lucky-twilight.glitch.me (open in Edge/IE)Refined
I
0

the first idea which came into mind is to use getBoundingClientRect (compatibility IE 4+)

The only issue is that if scrollbar will have 0px width as on Mac - this will not detect it


The second idea is to use getComputedStyle (compatibility: IE9+)

Please read the snippet code:

let outer = document.querySelector('.outer');
let inner = document.querySelector('.inner');
let pre = document.querySelector('pre');


// ***
// Bounding client rect detection
// ***
let posOuter = outer.getBoundingClientRect();
let posInner = inner.getBoundingClientRect();

let found = false;
if (posOuter.left !== posInner.left) {
    pre.innerHTML += "getBoundingClientRect detection: Scroll at left side\n";
    found = true;
}

if (posOuter.right !== posInner.right) {
    pre.innerHTML += "getBoundingClientRect detection: Scroll at right side\n";
    found = true;
}
if (!found) {
    pre.innerHTML += "getBoundingClientRect detection: Scroll in not found\n";
}

pre.innerHTML += "\n\n\n";

// ***
// getComputedStyle detection
// ***

let style = window.getComputedStyle(outer);
pre.innerHTML += "getComputedStyle detection: CSS direction is: " + style.getPropertyValue('direction') + "\n";
pre.innerHTML += "getComputedStyle detection: so scroll is on " + (style.getPropertyValue('direction') === 'rtl' ? 'left' : 'right') + ' side';
.outer{
   width: 100px;
   height: 100px;
   overflow-x: scroll;
   outline: 1px solid red; 
   border: none; /* !!! */;
}

.inner{
   height: 105px;
   box-sizing: border-box;
   border: 15px dashed green;
}
<div class="outer">
   <div class="inner">dummy</div>
</div>

<pre>
</pre>
Intelsat answered 27/9, 2018 at 13:35 Comment(6)
.getBoundingClientRect() won't work. In Edge, open any page, add dir="rtl" to <html> so the scrollbar will be on the left side, and then call document.documentElement.getBoundingClientRect(). left will be 0.Refined
have you checked getComputedStyle solution?Intelsat
The getBoundingClientRect detects difference between two DOM nodes. To measure single offset of document os course may not return what you needIntelsat
By the way, have you testef my snippet or used you own code to test proposed solution?Intelsat
Your snippet is not what I'm asking for. I'm asking about the detection of scrollbar position on <html dir="rtl">, not on random div element.Refined
@LimonMonte What about: var html = document.querySelector('html'); var style = window.getComputedStyle(html); var direction = style.direction; console.log(direction)Russophobe
E
0

document.elementFromPoint(-1, 0) returns the root element when its scrollbar is on the left side.

var scrollbarInfo = (function() {
    var body = document.body;
    var styleAttr = body.getAttribute("style") || "";
    body.setAttribute("style", styleAttr + ";overflow-y:scroll!important");

    var outer = body.appendChild(document.createElement("div"));
    outer.setAttribute("style", "position:fixed;overflow:scroll");
    var inner = outer.appendChild(document.createElement("div"));;

    try {
        return {
            width: outer.offsetWidth - inner.offsetWidth,
            outerPosition: document.elementFromPoint(-1, 0) ? "left" : "right",
            innerPosition: outer.getBoundingClientRect().left < inner.getBoundingClientRect().left ? "left" : "right",
        };
    } finally {
        body.setAttribute("style", styleAttr);
        body.removeChild(outer);
    }
})();

IE / Edge:

{outerPosition: "left", innerPosition: "left", width: 12}

Chrome / Firefox:

{outerPosition: "right", innerPosition: "left", width: 17}

Android / iOS:

{outerPosition: "right", innerPosition: "right", width: 0}

MacOS Safari:

{outerPosition: "right", innerPosition: "right", width: 0}
Epizoic answered 22/11, 2019 at 2:5 Comment(0)
C
-1

As far as I know, there is no way to do that. However, you can force the Scrollbar to the left side if you wrap it into a scrollable DIV. I'm not sure if that is what you want, but it works. (It works on my machine - I only could test it in Firefox, Chrome and Safari on Mac)

You can control the scrollbar with CSS. Changing the direction property will also change the scrollbar. And in Javascript you can react to all events or Attribute changes or what you want.

Here is a Boilerplate to play around with

const btn = document.querySelector('Button');
const html = document.querySelector('html');
const wrap = document.querySelector('#myBodyWrapper');
let state = 'ltr';
var inner = '';
for (let i = 0; i < 500; i++) {
  inner += 'Lorem ipsum Bla bla bla<br>';
}
wrap.innerHTML = inner;
btn.addEventListener('click', function() {
  state = (state === 'ltr') ? 'rtl' : 'ltr';
  wrap.style.direction = state;
}, false);
body {
  margin: 0;
  padding: 0;
  overflow: hidden;
}

div#myBodyWrapper {
  direction: ltr;
  overflow: auto;
  height: 100vh;
  width: 100vw;
}
button{
    background-color:gold;
    padding:5px;
    margin:5px 0;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>

<body>
  <button>Switch scrollbar</button>
  <div id="myBodyWrapper">

  </div>

</body>

</html>

Now you can control the scrollbar with CSS. And in Javascript you can react to all events or Attribute changes or what you want. You may want to use a mutationObserver on the HTML Element if you want to change the dir attribute and then react to that.

Cicero answered 9/10, 2018 at 16:22 Comment(1)
Sorry, but this is not answering my question.Refined

© 2022 - 2024 — McMap. All rights reserved.