Sprawdź, czy element HTML ma paski przewijania

113

Jaki jest najszybszy sposób sprawdzenia, czy element ma paski przewijania?

Jedną rzeczą jest oczywiście sprawdzenie, czy element jest większy niż jego rzutnia, co można łatwo zrobić, sprawdzając te dwie wartości:

el.scrollHeight > el.offsetHeight || el.scrollWidth > el.offsetWidth

ale to nie znaczy, że ma również paski przewijania (więc ludzie mogą go przewijać).

Pytanie

Jak mogę sprawdzić, czy paski przewijania w 1 przekroju przeglądarce i 2 tylko javascript (jak w żadnym jQuery ) sposób?

Tylko JavaScript, ponieważ potrzebuję jak najmniejszego narzutu, ponieważ chciałbym napisać bardzo szybki filtr selektora jQuery

// check for specific scrollbars
$(":scrollable(x/y/both)")

// check for ANY scrollbar
$(":scrollable")

Przypuszczam, że musiałbym sprawdzić overflowustawienia stylu, ale jak to zrobić w różnych przeglądarkach?

Dodatkowa edycja

Nie tylko overflowustawienia stylu. Sprawdzenie, czy element ma pasek przewijania, nie jest tak banalne, jak się wydaje. Pierwsza formuła, którą napisałem powyżej, działa dobrze, gdy element nie ma obramowania, ale jeśli tak jest (zwłaszcza gdy obramowanie ma znaczną szerokość), offsetwymiar może być większy niż scrollwymiar, ale element nadal można przewijać. W rzeczywistości musimy odjąć granice od offsetwymiaru, aby uzyskać rzeczywisty przewijalny obszar wyświetlania elementu i porównać go z scrollwymiarem.

Dla przyszłego odniesienia

:scrollableFiltr selektora jQuery jest zawarty w mojej .scrollintoview()wtyczce jQuery. Pełny kod można znaleźć w moim wpisie na blogu, jeśli ktoś go potrzebuje. Mimo że nie zapewniło właściwego rozwiązania, kod Soumyi znacznie pomógł mi rozwiązać problem. Wskazał mi właściwy kierunek.

Robert Koritnik
źródło

Odpowiedzi:

118

Znalazłem to gdzieś kilka tygodni temu. U mnie to zadziałało.

var div = document.getElementById('container_div_id');

var hasHorizontalScrollbar = div.scrollWidth > div.clientWidth;
var hasVerticalScrollbar = div.scrollHeight > div.clientHeight;

/* you'll get true/false */
jzhinga
źródło
12
Oczywiście miałeś uproszczony przykład. A co, jeśli twój pojemnik overflow:hiddensię na to postawi? Zawartość byłaby nadmiarowa, ale nadal nie można jej przewijać. Problem nie jest tak prosty, jak mogłoby się wydawać.
Robert Koritnik
7
Może to nie jest akceptowane rozwiązanie, ale zadziałało. Nie jestem pewien, dlaczego miałbyś go przewijać, jeśli jest ukryty.
vol7ron
Może to nie jest akceptowane rozwiązanie, ale zadziałało. Robert, w tych przypadkach nie mógłbyś również pobrać właściwości przepełnienia css, aby przetestować te przypadki (np. div.scrollHeight>div.clientHeight && !(div.style.overflow && div.style.overflow == 'hidden'))?
vol7ron
7
W wielu przypadkach się to nie udaje. Jeśli twój element ma przepełnienie: widoczny; szerokość: 200px; i ma element podrzędny o szerokości 500 pikseli, Twój element nie ma pasków przewijania, ale ma scrollWidth 500px i clientWidth 200px.
Joseph Lennox
1
Jeśli przepełnienie jest widoczne lub ukryte, zwykle nie ma pasków przewijania.
Hubert Grzeskowiak
16

Próbować:

W przypadku pionowego paska przewijania

el.scrollHeight> el.clientHeight

Poziomy pasek przewijania

el.scrollWidth> el.clientWidth

Wiem, że to działa przynajmniej dla IE8 i Firefox 3.6+.

Gary
źródło
2
Zwróciłem na to uwagę, że to mówi mi, że pewien element jest większy niż się wydaje, ale to nie znaczy, że wyświetla paski przewijania. Równie dobrze może mieć overflow:hiddeni nie można go już przewijać.
Robert Koritnik
1
Sprawdzanie z wartościami clientHeight / clientWidth nie daje dobrych wyników, ponieważ elementy mogą również mieć obramowania, które nie są uwzględnione w tej mierze. Sprawdź moją formułę. Działa lepiej niż twoje.
Robert Koritnik
To prawda, jeśli chodzi o używanie offsetHeight / offsetWidth do sprawdzania pasków przewijania zamiast clientHeight / clientWidth. Dzięki za zwrócenie uwagi.
Gary
@RobertKoritnik - więc po prostu sprawdź, czy ten konkretny element ma overflow:hiddenna sobie ... to nadal jest moim zdaniem poprawna odpowiedź, ponieważ jest najprostsza.
vsync
14

Może się to wydawać (lub być) trochę hakerskie, ale możesz przetestować właściwości scrollTopi scrollLeft.

Jeśli są większe niż 0, wiesz, że istnieją paski przewijania. Jeśli są 0, ustaw je na 1 i przetestuj je ponownie, aby zobaczyć, czy uzyskasz wynik 1. Następnie ustaw je z powrotem na 0.

Przykład: http://jsfiddle.net/MxpR6/1/

function hasScroll(el, direction) {
    direction = (direction === 'vertical') ? 'scrollTop' : 'scrollLeft';
    var result = !! el[direction];

    if (!result) {
        el[direction] = 1;
        result = !!el[direction];
        el[direction] = 0;
    }
    return result;
}

alert('vertical? ' + hasScroll(document.body, 'vertical'));
alert('horizontal? ' + hasScroll(document.body, 'horizontal'));

Wydaje mi się, że IE ma inną właściwość, więc za chwilę zaktualizuję to.

EDYCJA: Wygląda na to, że IE może obsługiwać tę właściwość. (Nie mogę teraz przetestować IE).

http://msdn.microsoft.com/en-us/library/ms534618(VS.85).aspx

user113716
źródło
Znacznie łatwiej jest sprawdzić paski przewijania, porównując offsetHeighti clientHeight. Ten ostatni jest zawsze mniejszy o rozmiar paska przewijania, gdy pasek przewijania jest obecny.
Robert Koritnik
@Robert: Nie jestem pewien, czy jest łatwiejsze, ale widzę, jak to działa. Wtedy zakładam, że jeśli element ma overflow:scroll, chcesz, aby był raportowany jako przewijalny, niezależnie od tego, czy paski przewijania są włączone, czy nie. Oczywiście moje rozwiązanie może mieć problemy, jeśli jest dołączone zdarzenie onscroll.
user113716
Nie całkiem. Jeśli sprawdzisz moją wtyczkę jQuery, muszę znaleźć rzeczywistego przewijalnego przodka, w przeciwnym razie nie mogę przewinąć elementu do widoku.
Robert Koritnik
8
@RobertKoritnik Nie prawda; niektóre przeglądarki mogą mieć paski przewijania, które nie zmniejszają szerokości. Na przykład na OS X. Lub dotykaj urządzeń.
daniel.gindi,
1
zawsze zwracaj fałsz
Fatih Erol
14

Oto jeszcze jedno rozwiązanie:

Jak zauważyło kilka osób, samo porównanie offsetHeight i scrollHeight nie wystarczy, ponieważ różnią się one elementami z ukrytym przepełnieniem itp., Które nadal nie mają pasków przewijania. Więc tutaj sprawdzam również, czy przepełnienie jest przewijane lub automatyczne w obliczonych stylach dla elementu:

var isScrollable = function(node) {
  var overflowY = window.getComputedStyle(node)['overflow-y'];
  var overflowX = window.getComputedStyle(node)['overflow-x'];
  return {
    vertical: (overflowY === 'scroll' || overflowY === 'auto') && node.scrollHeight > node.clientHeight,
    horizontal: (overflowX === 'scroll' || overflowX === 'auto') && node.scrollWidth > node.clientWidth,
  };
}
lotif
źródło
Jeśli overflow*jest scroll, zawsze będzie pasek przewijania, więc myślę, że chcesz vertical: overflowY === 'scroll' || overflowY === 'auto' && node.scrollHeight > node.clientHeight, itp. (Tj. Usuń nawiasy, aby scroll*vs client*był testowany tylko wtedy, gdy overflow*jest auto). W przeciwnym razie wydaje się, że jest to najbardziej poprawne rozwiązanie.
Jake,
@Jake Jeśli scrollHeight <clientHeight przy przepełnieniu przewijania, paski przewijania będą tam, ale będą wyłączone (tj. Elementu nie można przewijać). Myślę więc, że to zależy od aplikacji. Twoja modyfikacja jest prawidłowa, jeśli po prostu chcesz sprawdzić paski przewijania, bez względu na stan. Kiedy to wymyśliłem, chciałem zaimplementować automatyczne przewijanie komponentu, więc wyłączenie pasków przewijania jest w tym przypadku bezużyteczne. Wydaje się, że tak też jest w przypadku tego pytania (musi być „przewijane przez ludzi”)
lotif,
6

Może trochę spóźniam się na imprezę, ale ...

Uważam, że możesz wykryć paski przewijania za pomocą e.offsetWidth i e.clientWidth. Przesunięcie szerokości obejmuje krawędzie i paski przewijania, dopełnienie i szerokość. Szerokość klienta obejmuje wypełnienie i szerokość. Proszę zobaczyć:

https://developer.mozilla.org/en/DOM/element.offsetWidth (drugi obraz) https://developer.mozilla.org/en/DOM/element.clientWidth (drugi obraz)

Musisz sprawdzić:

  1. Określa, czy element ma przepełnienie ustawione na auto / scroll (w tym przepełnienieX / Y) przy użyciu stylu obliczonego / kaskadowego / bieżącego.
  2. Jeśli element ma przepełnienie ustawione na auto / scroll. Ustal offsetWidth i clientWidth.
  3. Jeśli clientWidth jest mniejsza niż offsetWidth - prawe obramowanie (znalezione ponownie przez styl obliczony / kaskadowy / bieżący), to wiesz, że masz pasek przewijania.

Zrób to samo dla pionu (offset / clientHeight).

IE7 zgłasza clientHeight 0 dla niektórych elementów (nie sprawdziłem dlaczego), dlatego zawsze potrzebujesz pierwszej kontroli przepełnienia.

Mam nadzieję że to pomoże!


źródło
3

W przypadku sprawdzania istnienia pasków przewijania występuje kilka problemów, z których jeden polega na tym, że w systemie Mac nie ma żadnego widocznego paska przewijania, więc oba powyższe rozwiązania nie dają dokładnej odpowiedzi.

Ponieważ renderowanie w przeglądarce nie jest zbyt częste, możesz sprawdzić przewijanie, zmieniając scroll, a następnie ustawiając go z powrotem:

const hasScrollBar = (element) => {
  const {scrollTop} = element;

  if(scrollTop > 0) {
    return true;
  }

  element.scrollTop += 10;

  if(scrollTop === element.scrollTop) {
    return false;
  }

  // undoing the change
  element.scrollTop = scrollTop;
  return true;
};

hakobpogh
źródło
1

Po prostu bawię się tutaj, ponieważ żadne z powyższych rozwiązań nie zadziałało (do tej pory). Odniosłem pewien sukces, porównując wysokość przewijania Div z jego offsetHeight

var oh = $('#wrapDiv').get(0).offsetHeight;
var sh = $('#wrapDiv').get(0).scrollHeight;

Wydaje mi się, że daje mi to dokładne porównanie ... do tej pory. Czy ktoś wie, czy to jest uzasadnione?

Michael
źródło
2
Myślę, że chcesz scrollHeight i clientHeight: stackoverflow.com/questions/4106538/ ...
bogate przypomnienie
1

W przypadku IE11 (Internet Explorer 11) musiałem zmienić logikę na:

// Subtract 3 (a small arbitrary number) to allow for IE reporting a difference of 1 when no scrollbar is present
var hasVerticalScrollbar = div.scrollHeight - 3 > div.clientHeight;

Dzieje się tak, ponieważ IE raportuje scrollHeight jako 1 większy niż clientHeight, gdy nie ma paska przewijania, ale ok. 9 większy, gdy obecny jest pasek przewijania

danday74
źródło
0

Jeśli chcesz wiedzieć, czy istnieje pasek przewijania dla całej strony i przy pełnej obsłudze przeglądarki , możesz użyć tego:

const hasScrollbar = document.body.scrollHeight > window.innerHeight

Ważne jest, aby używać window.innerHeightzamiast tego, document.body.clientHeightponieważ w niektórych przeglądarkach mobilnych clientHeight nie otrzyma rozmiaru paska adresu, ale scrollHeight tak, więc otrzymasz nieprawidłowe obliczenia.

Sebastian Hernandez
źródło
-5

żadna z tych odpowiedzi nie jest poprawna. musisz tego użyć:

var div = document.getElementById('container_div_id');

var hasHorizontalScrollbar = (div.offsetWidth > div.clientWidth);
var hasVerticalScrollbar = (div.offsetHeight > div.clientHeight);
hamid
źródło
To zostało odrzucone, ponieważ zostało skopiowane / wklejone z zaakceptowanej odpowiedzi sprzed 6 lat i nie potrzebujesz nawiasów, aby zmienić coś w wartość logiczną.
moto
-6

Dodaj do niego element o 100% szerokości. Następnie ustaw przepełnienie na ukryte. Jeśli styl obliczeniowy elementu (z jQ) ulegnie zmianie, rodzic miał pasek przewijania.

EDYCJA: Wygląda na to, że potrzebujesz metody obsługującej różne przeglądarki, takiej jak getComputedStyle . Próbować:

function getCSS(_elem, _style)
{
    var computedStyle;
    if (typeof _elem.currentStyle != 'undefined')
        computedStyle = _elem.currentStyle;
    else
        computedStyle = document.defaultView.getComputedStyle(_elem, null);
    return computedStyle[_style];
}
Soumya
źródło
1
Gdybym już używał do tego jQuery, wolałbym po prostu zrobić $(el).css("overflow/overflowX/overflowY")i sprawdzić, czy jest ustawiony na auto czy scroll . Ale chciałbym uniknąć używania jQuery.
Robert Koritnik
16
Style CSS NIE powie Ci, czy element ma paski przewijania , czy nie . Tylko czy może mieć paski przewijania, czy nie . Może znaleźć metodę określania szerokości elementu wewnętrznego w różnych przeglądarkach?
Soumya
@ Soumya92: Porównanie rozmiaru między przewijalnym a rzeczywistym rozmiarem jest trywialne i napisałem to powyżej ... Wszystko, co muszę teraz sprawdzić, to bieżące ustawienie przepełnienia dla konkretnego elementu.
Robert Koritnik
@ Soumya92: właśnie tego potrzebuję. Można to również bardzo uprościć, używając coalescecomputedStyle = el.currentStyle || document.defaultView.getComputedStyle(el, null);
Robert Koritnik
Jeszcze jedno: jak to działa na różnych przeglądarkach? Które przeglądarki są przez to obsługiwane?
Robert Koritnik