Как определить, с какой стороны браузера находится полоса прокрутки - справа или слева (в случае RTL)?

Для <html dir="rtl"> некоторые браузеры (Safari, Edge, IE) автоматически перемещают полосу прокрутки влево, что является правильным поведением:

введите здесь описание изображения

К сожалению, основные браузеры (Chrome и Firefox) ведут себя по-другому, полоса прокрутки по-прежнему находится в правой части браузера.

Можно ли определить программно (желательно с помощью vanilla JS), с какой стороны находится полоса прокрутки?


UPD (28.09.2018): пока в ответах нет рабочего решения, люди пытаются определить положение полосы прокрутки с помощью getBoundingClientRect().left или .offsetLeft, и это не будет работать, потому что всегда будет 0 в по крайней мере в Edge независимо от положения полосы прокрутки.


UPD (15.10.2018): TLDR: никак.

Кажется, что браузеры не предоставляют API для обнаружения стороны полосы прокрутки. В ответах ниже есть трюки с вставкой фиктивного div и вычислением, где находится полоса прокрутки в этом div. Я не могу рассматривать эти трюки как ответ, потому что полоса прокрутки документа может быть размещена несколькими способами (<html dir="rtl">, версия ОС RTL, настройки браузера и т. д.)


person Limon Monte    schedule 19.09.2018    source источник
comment
Совет: в Firefox можно заставить полосу прокрутки быть слева, изменив layout.scrollbar.side на 3 в about:config.   -  person Limon Monte    schedule 21.09.2018
comment
Пожалуйста, взгляните на github.com/shadiabuhilal/rtl-detect. простой способ обнаружения. Надеюсь, это даст некоторое представление.   -  person Observer    schedule 21.09.2018
comment
спасибо за ваш ответ @Observer, но эта библиотека не совсем то, что я ищу.   -  person Limon Monte    schedule 21.09.2018
comment
Вы можете определить, какой язык используется на вашем веб-сайте, используя эту библиотеку, а затем установить что-то вроде этого *{direction: rtl}   -  person Observer    schedule 21.09.2018
comment
Я пытаюсь понять проблему, с которой вы столкнулись. Интересно узнать, для чего он вам нужен?   -  person Gabriel C. Troia    schedule 10.10.2018
comment
Просто мысли вслух, но не было бы проще обнаружить браузер (из пользовательского агента) и сопоставить его с поведением полосы прокрутки (справа/слева) на основе этого? Что-то вроде простой хэш-карты: {chrome: 'right', firefox: 'right', edge: 'left', safari: 'left', ie: 'left'}   -  person Gabriel C. Troia    schedule 10.10.2018
comment
@GabrielC.Troia › Я пытаюсь понять проблему, с которой вы столкнулись. Интересно узнать, для чего он вам нужен? github.com/sweetalert2/sweetalert2/issues/1221   -  person Limon Monte    schedule 11.10.2018
comment
Вам нужен атрибут dir обязательно в теге html, или в теге body может быть нормально, если он всегда размещает полосу прокрутки слева (в случае rtl)?   -  person ElJackiste    schedule 11.10.2018
comment
Се му инновационный ответ. может быть, это могло бы помочь вам.   -  person AmerllicA    schedule 11.10.2018


Ответы (6)


Как показано здесь, полоса прокрутки победила не переходить на другую сторону с dir="rtl" в HTML-теге html или body в Firefox, Chrome, Opera и Safari. Он работает в IE и Edge. Я пробовал (в Edge 17, IE 11, Firefox 62, Chrome 69, Opera 56, Safari 5.1), и это все еще актуально в октябре 2018 года (Safari> 9.1 отображает полосу прокрутки слева, если dir="rtl").

До сих пор я не нашел никакого кросс-браузерного нативного решения, чтобы найти положение полосы прокрутки, как вы просили. Я не думаю, что у вас есть один. Мы можем только попытаться найти "хак", чтобы исправить это...

Основываясь на вашем вопросе/комментариях и вашей реальной проблеме здесь, я думаю, что это будет лучше ориентироваться на совместимость 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
  • Край => все
  • Firefox => v4 - 2011
  • Опера => v10 - 2009
  • Хром => v19 - 2012
  • Сафари => v10.1 - 2016

В совместимых браузерах вы можете «доверять» положению полосы прокрутки на основе атрибута dir. Это означает, что для браузеров, перечисленных выше, полоса прокрутки будет слева для dir="rtl" и справа для dir="ltr". Я проверил, и это работает.

Для более старых версий браузеров у меня нет исправления на данный момент. Это означает, что вы не будете полностью совместимы, но, как видите, в основном это проблема с сафари.

РЕДАКТИРОВАТЬ: Найти положение полосы прокрутки

Как я упоминал выше, я думаю, что мы можем попытаться найти «хак», чтобы найти положение полосы прокрутки. Я не эксперт по css, поэтому мое решение не очень красивое. Кто-то с навыками css, вероятно, мог бы найти хорошее решение.

Если прокрутка находится на теле (решение выше), мы можем определить положение полосы прокрутки с помощью элемента css position. Например этот код:

<!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
  • Край => все
  • Firefox => v4 - 2011
  • Опера => v10 - 2009
  • Хром => v15 - 2011
  • Safari => не работает

Это решение не очень полезно, если прокрутка находится на теге body, потому что, как упоминалось выше, в этом случае мы можем «доверять» атрибуту dir. Я поместил его здесь, потому что его можно вероятно адаптировать для прокрутки на теге html.

person ElJackiste    schedule 11.10.2018

Я улучшил 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>

person yunzen    schedule 10.10.2018
comment
window.getComputedStyle не сработает. Положение полосы прокрутки браузера может определяться не только сайтом. Его можно установить для всей системы (например, для арабского языка Windows) или в настройках браузера (например, для layout.scrollbar.side в Firefox установлено значение 3). - person Limon Monte; 10.10.2018

первая идея, которая пришла в голову, — использовать 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>

person Andrii Muzalevskyi    schedule 27.09.2018
comment
.getBoundingClientRect() не сработает. В Edge откройте любую страницу, добавьте dir="rtl" к <html>, чтобы полоса прокрутки была слева, а затем вызовите document.documentElement.getBoundingClientRect(). left будет 0. - person Limon Monte; 27.09.2018
comment
Вы проверили решение getComputedStyle? - person Andrii Muzalevskyi; 30.09.2018
comment
getBoundingClientRect обнаруживает разницу между двумя узлами DOM. Чтобы измерить одно смещение документа, курс ОС может не вернуть то, что вам нужно - person Andrii Muzalevskyi; 30.09.2018
comment
Кстати, вы тестировали мой фрагмент или использовали свой код для тестирования предложенного решения? - person Andrii Muzalevskyi; 30.09.2018
comment
Ваш фрагмент не то, что я прошу. Я спрашиваю об определении положения полосы прокрутки на <html dir="rtl">, а не на случайном элементе div. - person Limon Monte; 01.10.2018
comment
@LimonMonte А как насчет: var html = document.querySelector('html'); var style = window.getComputedStyle(html); var direction = style.direction; console.log(direction) - person yunzen; 10.10.2018

Это не очень надежное решение, но его можно использовать в качестве основы для создания лучшего решения.

Основная логика заключается в том, что дочерние элементы смещаются вправо, когда с левой стороны элемента-контейнера есть полоса прокрутки. Все, что нам нужно сделать, это измерить этот сдвиг по наличию и отсутствию полосы прокрутки.

Вот простая функция, основанная на 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;
}
person 11thdimension    schedule 21.09.2018
comment
Извините @11thdimension, но это не кроссбраузерное решение, не работает в Microsoft Edge и IE, доказательство: lucky-twilight .glitch.me (открыть в Edge/IE) - person Limon Monte; 22.09.2018

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/край:

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

Хром/Файрфокс:

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

Android / iOS:

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

Сафари для MacOS:

{outerPosition: "right", innerPosition: "right", width: 0}
person Merci chao    schedule 22.11.2019

Насколько я знаю, нет никакого способа сделать это. Однако вы можете переместить полосу прокрутки влево, если поместите ее в прокручиваемый DIV. Я не уверен, что это то, что вы хотите, но это работает. (Он работает на моей машине — я смог протестировать его только в Firefox, Chrome и Safari на Mac)

Вы можете управлять полосой прокрутки с помощью CSS. Изменение свойства direction также изменит полосу прокрутки. А в Javascript вы можете реагировать на все события или изменения атрибутов или на то, что вы хотите.

Вот Boilerplate, чтобы поиграть с

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 вы можете реагировать на все события или изменения атрибутов или на то, что вы хотите. Вы можете использовать мутациюObserver для HTML-элемента, если хотите изменить атрибут dir, а затем отреагировать на это.

person Andre    schedule 09.10.2018
comment
Извините, но это не ответ на мой вопрос. - person Limon Monte; 11.10.2018