如何检测浏览器滚动条的位置 - 是在右边还是左边(在RTL的情况下)?

28

对于<html dir="rtl">,一些浏览器(Safari、Edge、IE)会自动将滚动条移动到左侧,这是正确的行为:

enter image description here

不幸的是,主流浏览器(Chrome和Firefox)的行为不同,滚动条仍在浏览器右侧。

是否有可能通过编程(最好使用纯JS)检测滚动条位于哪一侧?


更新(2018年9月28日):目前尚无答案提供可行解决方案,人们试图使用getBoundingClientRect().left.offsetLeft来检测滚动条位置,但这种方法在Edge中始终为0而与滚动条位置无关。


更新(2018年10月15日):简而言之,无法实现。

似乎浏览器没有提供检测滚动条位置的API。下面的答案中有插入虚拟

并计算其中滚动条位置的技巧。鉴于文档滚动条可以用多种方式定位(如<html dir="rtl">、操作系统的RTL版本、浏览器设置等),我不能将这些技巧视为答案。


1
提示:在Firefox中,可以通过在“about:config”中将“layout.scrollbar.side”更改为“3”来强制滚动条位于左侧。 - Limon Monte
请查看 https://github.com/shadiabuhilal/rtl-detect 看起来没有简单的方法可以检测。希望它能提供一些想法。 - Observer
谢谢您的回复,@Observer,但那个库并不是我正在寻找的。 - Limon Monte
您可以使用那个库来检测您的网站上使用的语言,然后设置类似于*{direction:rtl}这样的东西。 - Observer
@GabrielC.Troia 我正在努力理解你所面临的问题。想知道你需要它做什么? https://github.com/sweetalert2/sweetalert2/issues/1221 - Limon Monte
显示剩余4条评论
6个回答

4
此处所示,在Firefox,Chrome,Opera和Safari中,在htmlbody HTML标记上使用dir="rtl"时,滚动条不会改变位置。 它在IE和Edge上运行。我尝试了(Edge 17,IE 11,Firefox 62,Chrome 69,Opera 56,Safari 5.1),并且在2018年10月仍然相关(如果dir="rtl",则Safari > 9.1将显示左侧的滚动条)。
到目前为止,我没有找到任何跨浏览器的本地解决方案查找滚动条的位置,正如您所要求的那样。 我认为您也没有。我们只能尝试找到“hack”来解决它...
基于您的问题/评论以及这里的实际问题,我认为更好的方法是关注dir="rtl"的兼容性。 您正在尝试找出滚动条的位置,因为它一开始无法正常工作。 为了使其按预期工作,一个快速修复方法是将滚动条放置在body元素上:

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

这段 CSS 代码能够使得 body 元素滚动,而不是 html 元素。奇怪的是,在 body 上设置滚动会在正确的位置显示滚动条。这在当前版本的浏览器中有效。
已测试可用的浏览器及其最早版本如下:
  • IE => v6 - 2001
  • Edge => 所有版本
  • Firefox => v4 - 2011
  • Opera => v10 - 2009
  • Chrome => v19 - 2012
  • Safari => v10.1 - 2016
对于兼容的浏览器,您可以根据 dir 属性“信任”滚动条位置。这意味着对于上面列出的浏览器,当 dir="rtl" 时滚动条将位于左侧,当 dir="ltr" 时滚动条将位于右侧。经过测试,它有效。
对于旧版本的浏览器,目前我还没有解决方案。这意味着您不能完全兼容,但主要问题是 Safari,正如您所看到的。
编辑:寻找滚动条位置的解决方案 如上所述,我认为我们可以尝试找到一个“技巧”来查找滚动条的位置。我不是 CSS 专家,所以我的解决方案不太完美。具有 CSS 技能的人可能会找到一个好的解决方案。
如果滚动在 body 上(上面的解决方案),我们可以通过 CSS 定位元素检测滚动条位置。例如,以下代码:

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

兼容性情况(测试的最旧工作版本):
- IE => v6 - 2001 - Edge => 所有版本 - Firefox => v4 - 2011 - Opera => v10 - 2009 - Chrome => v15 - 2011 - Safari => 不可用
如果滚动条在 body 标签上,这个解决方案并不是很有用,因为如上所述,在这种情况下我们可以“信任”dir属性。我把它放在这里是因为它可能可以适用于滚动在 html 标签上的情况。

2

我对Andre答案进行了改进:

你只需要使用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>


1
window.getComputedStyle 不起作用。浏览器滚动条的位置不仅可以由网站确定,还可以在系统范围内设置(例如阿拉伯语 Windows)或在浏览器设置中设置(例如 Firefox 的 layout.scrollbar.side 设置为 3)。 - Limon Monte

0

脑海中最先想到的是使用getBoundingClientRect(兼容IE 4+)

唯一的问题是,如果滚动条宽度为0像素,例如在Mac上,这将无法检测到


第二个想法是使用getComputedStyle(兼容性:IE9+)

请阅读代码片段:

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>


.getBoundingClientRect() 不起作用。在 Edge 中,打开任何页面,在 <html> 中添加 dir="rtl",这样滚动条就会出现在左侧,然后调用 document.documentElement.getBoundingClientRect()left 将为 0 - Limon Monte
你有检查过 getComputedStyle 的解决方案吗? - Andrii Muzalevskyi
getBoundingClientRect可以检测两个DOM节点之间的差异。当然,为了测量文档的单个偏移量可能无法返回您所需的内容。 - Andrii Muzalevskyi
顺便问一下,你有测试过我的代码片段还是用自己的代码来测试提出的解决方案? - Andrii Muzalevskyi
你的代码片段不是我所要求的。我询问的是如何检测 <html dir="rtl"> 上滚动条的位置,而不是随机的 div 元素。 - Limon Monte
2
@LimonMonte 你觉得这段代码怎么样:var html = document.querySelector('html'); var style = window.getComputedStyle(html); var direction = style.direction; console.log(direction) - yunzen

0

这不是一个非常健壮的解决方案,但可以用作创建更好解决方案的基础。

基本逻辑是当容器元素左侧有滚动条时,子元素向右移动。我们所要做的就是在存在和不存在滚动条的情况下测量该移动。

以下是基于jQuery的简单函数,用于执行此操作。scrollElement是我们需要检测滚动条位置的元素。大多数情况下应该为html/body,但不一定,我不知道任何简单的方法来找出它。

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

抱歉@11thdimension,但这不是跨浏览器的解决方案,在Microsoft Edge和IE中无法工作,证明:https://lucky-twilight.glitch.me/(在Edge / IE中打开) - Limon Monte

0

document.elementFromPoint(-1, 0) 在滚动条在左侧时返回根元素。

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}

安卓 / iOS:

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

MacOS Safari:

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

-1
据我所知,目前没有办法做到这一点。但是,如果您将滚动条包装到可滚动的 DIV 中,您可以强制将其滚动到左侧。我不确定这是否符合您的要求,但它有效。
(在我的机器上测试过,在 Mac 上的 Firefox、Chrome 和 Safari 上测试过)
您可以使用 CSS 控制滚动条。更改 direction 属性也会更改滚动条。而且在 JavaScript 中,您可以对所有事件或属性更改做出反应,或者您想要的任何内容。
以下是一个样板文件,供您玩耍。

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>

现在你可以使用CSS控制滚动条。而在JavaScript中,你可以对所有事件或属性更改做出反应,或者你想要的任何东西。 如果你想要改变dir属性并对其做出反应,你可能需要在HTML元素上使用mutationObserver。

抱歉,但这并没有回答我的问题。 - Limon Monte

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接