Como posso verificar se uma barra de rolagem está visível?

277

É possível verificar o overflow:autode uma div?

Por exemplo:

HTML

<div id="my_div" style="width: 100px; height:100px; overflow:auto;" class="my_class"> 
  * content
</div>

JQUERY

$('.my_class').live('hover', function (event)
{
    if (event.type == 'mouseenter')
    {
         if( ...  if scrollbar visible ? ... )
         {
            alert('true'):
         }
         else
         {
            alert('false'):
         }
    }

});

Às vezes, o conteúdo é curto (sem barra de rolagem) e às vezes longo (barra de rolagem visível).

Peter
fonte

Respostas:

376

um pequeno plugin para ele.

(function($) {
    $.fn.hasScrollBar = function() {
        return this.get(0).scrollHeight > this.height();
    }
})(jQuery);

use assim,

$('#my_div1').hasScrollBar(); // returns true if there's a `vertical` scrollbar, false otherwise..

testado trabalhando no Firefox, Chrome, IE6,7,8

mas não está funcionando corretamente no bodyseletor de tags

demonstração


Editar

Eu descobri que quando você tem uma barra de rolagem horizontal que faz com que a barra de rolagem vertical apareça, essa função não funciona ....

Eu descobri outra solução ... use clientHeight

return this.get(0).scrollHeight > this.get(0).clientHeight;
Reigel
fonte
22
Se você têm estofo você precisa usar > this.innerHeight(); jsfiddle.net/p3FFL/210
jcubic
2
Há um problema com isso, se uma barra de rolagem horizontal também existir, isso retornará falso, mesmo que exista uma barra de rolagem vertical até a altura ter sido reduzida pela altura da barra de rolagem horizontal.
Ally
por que você definiu a mesma função duas vezes? @jcubic
Nitin Sawant
9
Observe que nos Macs a barra de rolagem flutua sobre o conteúdo e desaparece quando não está em uso. No Windows, é sempre visível e ocupa espaço horizontal. Portanto, apenas porque o conteúdo pode ser rolado (detectado por esta função) não significa que uma barra de rolagem esteja necessariamente presente.
Andrew Andrew
2
(function ($) {$ .fn.hasScrollBar = function () {retorne this.get (0) .scrollWidth> this.width); }}) (jQuery); isso funciona para a sobrefluxo horizontal. É bom para verificar a capacidade de resposta móvel em sites em iframes.
Alexander Nicholas Popa
57

Talvez uma solução mais simples.

if ($(document).height() > $(window).height()) {
    // scrollbar
}
Sean Zheng
fonte
Esta resposta funcionou para mim depois de verificar se o DOM está pronto usando JQuery.ready()
Simple Sandman
4
Isso pressupõe que a barra de rolagem esteja na janela e não um elemento div. Propor a alteração da referência para sugerir: "Se você só precisa testar a barra de rolagem na janela principal, pode tentar:"
justdan23
43

Você pode fazer isso usando uma combinação do Element.scrollHeighte Element.clientHeightatributos.

De acordo com a MDN:

O atributo somente leitura Element.scrollHeight é uma medida da altura do conteúdo de um elemento, incluindo o conteúdo não visível na tela devido ao estouro. O valor scrollHeight é igual ao clientHeight mínimo que o elemento exigiria para caber todo o conteúdo no ponto de vista sem usar uma barra de rolagem vertical. Inclui o preenchimento do elemento, mas não a sua margem.

E:

A propriedade somente leitura Element.clientHeight retorna a altura interna de um elemento em pixels, incluindo preenchimento, mas não a altura, borda ou margem da barra de rolagem horizontal.

clientHeight pode ser calculado como altura CSS + preenchimento CSS - altura da barra de rolagem horizontal (se presente).

Portanto, o elemento exibirá uma barra de rolagem se a altura da rolagem for maior que a altura do cliente, portanto, a resposta para sua pergunta é:

function scrollbarVisible(element) {
  return element.scrollHeight > element.clientHeight;
}
Ajedi32
fonte
2
exemplo: github.com/twbs/bootstrap/blob/master/js/modal.js#L242 e +1 para a cotação e explicação do MDN!
lowtechsun
no chrome se o conteúdo tiver uma borda que não esteja incluída no scrollHeight
AT
1
Votado com sucesso por não usar a estrutura / biblioteca.
John
Cuidado - isso causa um reflow, que é uma perda de desempenho. gist.github.com/paulirish/5d52fb081b3570c81e3a
Todd Sjolander
43

Eu deveria mudar um pouco do que Reigel disse:

(function($) {
    $.fn.hasScrollBar = function() {
        return this[0] ? this[0].scrollHeight > this.innerHeight() : false;
    }
})(jQuery);

innerHeight conta a altura do controle e seus preenchimentos superior e inferior

kpull1
fonte
return (this.get (0))? this.get (0) .scrollHeight> this.innerHeight (): false;
commonpike
3
Eu acho que isso deve ser atribuído como a resposta certa. Isso funcionou no FF35, IE11 e Chrome39.
precisa saber é o seguinte
Ele não verifica o valor de 'estouro' para garantir que as barras de rolagem apareçam quando a condição scrollHeight for atendida.
BT
1
@ BT Mas se eu tiver o estouro definido como automático no meu css, não preciso dessa verificação extra? Ele compara tamanhos e é o suficiente ...?
Andrew
Uma resposta que só funciona para você não é uma resposta .. como você sabe o que as outras pessoas têm no CSS? Sua resposta não menciona essa limitação. Se alguém não consegue responder à sua resposta e fazê-la funcionar, não é uma boa resposta.
BT
27

Isso se expande na resposta de @ Reigel. Ele retornará uma resposta para as barras de rolagem horizontais ou verticais.

(function($) {
    $.fn.hasScrollBar = function() {
        var e = this.get(0);
        return {
            vertical: e.scrollHeight > e.clientHeight,
            horizontal: e.scrollWidth > e.clientWidth
        };
    }
})(jQuery);

Exemplo:

element.hasScrollBar()             // Returns { vertical: true/false, horizontal: true/false }
element.hasScrollBar().vertical    // Returns true/false
element.hasScrollBar().horizontal  // Returns true/false
the4tress
fonte
11

Você precisa de element.scrollHeight . Compare com $(element).height().

Boldewyn
fonte
8

Criei um novo pseudo-seletor customizado para jQuery para testar se um item tem uma das seguintes propriedades css:

  1. estouro: [scroll | auto]
  2. estouro-x: [rolagem | auto]
  3. estouro-y: [rolagem | auto]

Eu queria encontrar o pai rolável mais próximo de outro elemento, então também escrevi outro pequeno plugin do jQuery para encontrar o pai mais próximo com estouro.

Essa solução provavelmente não apresenta o melhor desempenho, mas parece funcionar. Eu usei em conjunto com o plugin $ .scrollTo. Às vezes, preciso saber se um elemento está dentro de outro contêiner rolável. Nesse caso, eu quero rolar o elemento rolável pai contra a janela.

Provavelmente eu deveria ter empacotado isso em um único plug-in e adicionado o seletor psuedo como parte do plug-in, além de expor um método 'mais próximo' para encontrar o contêiner rolável (pai) mais próximo.

Anywho .... aqui está.

Plugin $ .isScrollable jQuery:

$.fn.isScrollable = function(){
    var elem = $(this);
    return (
    elem.css('overflow') == 'scroll'
        || elem.css('overflow') == 'auto'
        || elem.css('overflow-x') == 'scroll'
        || elem.css('overflow-x') == 'auto'
        || elem.css('overflow-y') == 'scroll'
        || elem.css('overflow-y') == 'auto'
    );
};

$ (': scrollable') pseudo-seletor jQuery:

$.expr[":"].scrollable = function(a) {
    var elem = $(a);
    return elem.isScrollable();
};

Plug-in jQuery $ .scrollableparent ():

$.fn.scrollableparent = function(){
    return $(this).closest(':scrollable') || $(window); //default to $('html') instead?
};

A implementação é bem simples

//does a specific element have overflow scroll?
var somedivIsScrollable = $(this).isScrollable();
//use :scrollable psuedo selector to find a collection of child scrollable elements
var scrollableChildren = $(this).find(':scrollable');
//use $.scrollableparent to find closest scrollable container
var scrollableparent = $(this).scrollableparent();

ATUALIZAÇÃO: Descobri que Robert Koritnik já tinha um pseudo-seletor muito mais poderoso: rolagem que identificará os eixos rolantes e a altura dos contêineres roláveis, como parte de seu plugin jQuery $ .scrollintoview (). scrollintoview plugin

Aqui está seu pseudo-seletor chique (adereços):

    $.extend($.expr[":"], {

    scrollable: function (element, index, meta, stack) {

        var direction = converter[typeof (meta[3]) === "string" && meta[3].toLowerCase()] || converter.both;

        var styles = (document.defaultView && document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(element, null) : element.currentStyle);

        var overflow = {

            x: scrollValue[styles.overflowX.toLowerCase()] || false,

            y: scrollValue[styles.overflowY.toLowerCase()] || false,

            isRoot: rootrx.test(element.nodeName)

        };



        // check if completely unscrollable (exclude HTML element because it's special)

        if (!overflow.x && !overflow.y && !overflow.isRoot)

        {

            return false;

        }



        var size = {

            height: {

                scroll: element.scrollHeight,

                client: element.clientHeight

            },

            width: {

                scroll: element.scrollWidth,

                client: element.clientWidth

            },

            // check overflow.x/y because iPad (and possibly other tablets) don't dislay scrollbars

            scrollableX: function () {

                return (overflow.x || overflow.isRoot) && this.width.scroll > this.width.client;

            },

            scrollableY: function () {

                return (overflow.y || overflow.isRoot) && this.height.scroll > this.height.client;

            }

        };

        return direction.y && size.scrollableY() || direction.x && size.scrollableX();

    }

});
thegrandoverseer
fonte
6

A primeira solução acima funciona apenas no IE A segunda solução acima funciona apenas no FF

Essa combinação de ambas as funções funciona nos dois navegadores:

//Firefox Only!!
if ($(document).height() > $(window).height()) {
    // has scrollbar
    $("#mtc").addClass("AdjustOverflowWidth");
    alert('scrollbar present - Firefox');
} else {
    $("#mtc").removeClass("AdjustOverflowWidth");
}

//Internet Explorer Only!!
(function($) {
    $.fn.hasScrollBar = function() {
        return this.get(0).scrollHeight > this.innerHeight();
    }
})(jQuery);
if ($('#monitorWidth1').hasScrollBar()) {
    // has scrollbar
    $("#mtc").addClass("AdjustOverflowWidth");
    alert('scrollbar present - Internet Exploder');
} else {
    $("#mtc").removeClass("AdjustOverflowWidth");
}​
  • Embrulhe em um documento pronto
  • monitorWidth1: a div em que o estouro é definido como automático
  • mtc: uma div de contêiner dentro de monitorWidth1
  • AdjustOverflowWidth: uma classe css aplicada à div #mtc quando a barra de rolagem está ativa * Use o alerta para testar o navegador cruzado e, em seguida, comente o código de produção final.

HTH

Aaron Hopkins
fonte
6

(scrollWidth / Height - clientWidth / Height) é um bom indicador da presença de uma barra de rolagem, mas fornecerá uma resposta "falso positivo" em várias ocasiões. se você precisar ser preciso, sugiro usar a seguinte função. em vez de tentar adivinhar se o elemento é rolável - você pode rolar ...

function isScrollable( el ){
  var y1 = el.scrollTop;
  el.scrollTop  += 1;
  var y2 = el.scrollTop;
  el.scrollTop  -= 1;
  var y3 = el.scrollTop;
  el.scrollTop   = y1;
  var x1 = el.scrollLeft;
  el.scrollLeft += 1;
  var x2 = el.scrollLeft;
  el.scrollLeft -= 1;
  var x3 = el.scrollLeft;
  el.scrollLeft  = x1;
  return {
    horizontallyScrollable: x1 !== x2 || x2 !== x3,
    verticallyScrollable: y1 !== y2 || y2 !== y3
  }
}
function check( id ){
  alert( JSON.stringify( isScrollable( document.getElementById( id ))));
}
#outer1, #outer2, #outer3 {
  background-color: pink;
  overflow: auto;
  float: left;
}
#inner {
  width:  150px;
  height: 150px;
}
button {  margin: 2em 0 0 1em; }
<div id="outer1" style="width: 100px; height: 100px;">
  <div id="inner">
    <button onclick="check('outer1')">check if<br>scrollable</button>
  </div>
</div>
<div id="outer2" style="width: 200px; height: 100px;">
  <div id="inner">
    <button onclick="check('outer2')">check if<br>scrollable</button>
  </div>
</div>
<div id="outer3" style="width: 100px; height: 180px;">
  <div id="inner">
    <button onclick="check('outer3')">check if<br>scrollable</button>
  </div>
</div>

Yair Levy
fonte
Em que ocasiões isso dará um falso positivo?
GaloisGirl #
5

Ugh, as respostas de todos aqui estão incompletas e vamos parar de usar o jquery nas respostas do SO, por favor. Verifique a documentação do jquery se você quiser informações sobre o jquery.

Aqui está uma função generalizada de javascript puro para testar se um elemento possui ou não barras de rolagem de uma maneira completa:

// dimension - Either 'y' or 'x'
// computedStyles - (Optional) Pass in the domNodes computed styles if you already have it (since I hear its somewhat expensive)
function hasScrollBars(domNode, dimension, computedStyles) {
    dimension = dimension.toUpperCase()
    if(dimension === 'Y') {
        var length = 'Height'
    } else {
        var length = 'Width'
    }

    var scrollLength = 'scroll'+length
    var clientLength = 'client'+length
    var overflowDimension = 'overflow'+dimension

    var hasVScroll = domNode[scrollLength] > domNode[clientLength]


    // Check the overflow and overflowY properties for "auto" and "visible" values
    var cStyle = computedStyles || getComputedStyle(domNode)
    return hasVScroll && (cStyle[overflowDimension] == "visible"
                         || cStyle[overflowDimension] == "auto"
                         )
          || cStyle[overflowDimension] == "scroll"
}
BT
fonte
4
Por que evitar o uso de jquery em uma pergunta exibida como jquery? Por favor, adicione links para a parte da documentação do jquery mencionada.
kpull1
6
@ kpull1 Muitas pessoas marcam o jQuery em todas as perguntas javascript que elas têm. Esta questão tem 0 relação com jQuery. Não é nenhuma parte da documentação do jQuery que tem a resposta, porque jQuery não faz isso, nem deve.
BT
4

Vou estender isso ainda mais para aquelas pobres almas que, como eu, usam uma das estruturas js modernas e não a JQuery e foram totalmente abandonadas pelas pessoas desse segmento:

isso foi escrito no Angular 6, mas se você escrever React 16, Vue 2, Polymer, Ionic, React-Native, saberá o que fazer para adaptá-lo. E é todo o componente, por isso deve ser fácil.

import {ElementRef, AfterViewInit} from '@angular/core';

@Component({
  selector: 'app',
  templateUrl: './app.html',
  styleUrls: ['./app.scss']
})
export class App implements AfterViewInit {
scrollAmount;

constructor(
  private fb: FormBuilder,
  private element: ElementRef 
) {}

ngAfterViewInit(){
  this.scrollAmount = this.element.nativeElement.querySelector('.elem-list');
  this.scrollAmount.addEventListener('wheel', e => { //you can put () instead of e
  // but e is usefull if you require the deltaY amount.
    if(this.scrollAmount.scrollHeight > this.scrollAmount.offsetHeight){
       // there is a scroll bar, do something!
    }else{
       // there is NO scroll bar, do something!
    }
  });
}
}

no html, haveria uma div com a classe "elem-list" estilizada no css ou scss para ter um heighte um overflowvalor que não é hidden. (mais autoou menos sroll)

Ativei essa avaliação em um evento de rolagem porque meu objetivo final era ter "rolagens de foco automáticas" que decidem se estão rolando o conjunto inteiro de componentes horizontalmente se esses componentes não tiverem rolagem vertical disponível e, caso contrário, rolem apenas as entranhas de uma das componentes verticalmente.

mas você pode colocar a avaliação em outro lugar para que ela seja acionada por outra coisa.

o importante a lembrar aqui, é que você nunca é forçado a voltar a usar o JQuery, sempre há uma maneira de acessar as mesmas funcionalidades que ele possui sem usá-lo.

tatsu
fonte
1
Curioso para saber por que você está ouvindo eventos de roda para verificar se há uma barra de rolagem ou não.
Mix3d 01/10/1918
3
Além disso, como você está usando funções de seta, thismantém o escopo pai; th = this;é desnecessário.
Mix3d 01/10/1918
1
@ mix3d Eu pessoalmente uso este código para alternar automaticamente entre a rolagem horizontal e vertical com base no que tem direção de rolagem na determinado elemento pontiagudo que é dinâmico
tatsu
1
Re: isso; é basicamente açúcar sintático (mais agradável taquigrafia) parafunction(){}.bind(this)
mix3d
1

Aqui está uma versão aprimorada da resposta de Evan, que parece explicar adequadamente a lógica de estouro.

            function element_scrollbars(node) {
                var element = $(node);
                var overflow_x = element.css("overflow-x");
                var overflow_y = element.css("overflow-y");
                var overflow = element.css("overflow");
                if (overflow_x == "undefined") overflow_x == "";
                if (overflow_y == "undefined") overflow_y == "";
                if (overflow == "undefined") overflow == "";
                if (overflow_x == "") overflow_x = overflow;
                if (overflow_y == "") overflow_y = overflow;
                var scrollbar_vertical = (
                    (overflow_y == "scroll")
                    || (
                        (
                            (overflow_y == "hidden")
                            || (overflow_y == "visible")
                        )
                        && (
                            (node.scrollHeight > node.clientHeight)
                        )
                    )
                );
                var scrollbar_horizontal = (
                    (overflow_x == "scroll")
                    || (
                        (
                            (overflow_x == "hidden")
                            || (overflow_x == "visible")
                        )
                        && (
                            (node.scrollWidth > node.clientWidth)
                        )
                    )
                );
                return {
                    vertical: scrollbar_vertical,
                    horizontal: scrollbar_horizontal
                };
            }
JBlitzen
fonte
1

As soluções fornecidas acima funcionarão na maioria dos casos, mas a verificação do scrollHeight e do overflow às vezes não é suficiente e pode falhar nos elementos body e html, como visto aqui: https://codepen.io/anon/pen/EvzXZw

1. Solução - verifique se o elemento é rolável:

function isScrollableY (element) {
  return !!(element.scrollTop || (++element.scrollTop && element.scrollTop--));
}

Nota: os elementos com overflow: hiddentambém são tratados como roláveis ​​( mais informações ); portanto, você pode adicionar uma condição a essa também, se necessário:

function isScrollableY (element) {
    let style = window.getComputedStyle(element);
    return !!(element.scrollTop || (++element.scrollTop && element.scrollTop--)) 
           && style["overflow"] !== "hidden" && style["overflow-y"] !== "hidden";
}

Tanto quanto sei, esse método só falha se o elemento tiver scroll-behavior: smooth.

Explicação: O truque é que a tentativa de rolar para baixo e revertê-lo não será renderizada pelo navegador. A função superior também pode ser escrita da seguinte maneira:

2. Solução - faça todas as verificações necessárias:

function isScrollableY (element) {
  const style = window.getComputedStyle(element);
  
  if (element.scrollHeight > element.clientHeight &&
      style["overflow"] !== "hidden" && style["overflow-y"] !== "hidden" &&
      style["overflow"] !== "clip" && style["overflow-y"] !== "clip"
  ) {
    if (element === document.documentElement) return true;
    else if (style["overflow"] !== "visible" && style["overflow-y"] !== "visible") {
      // special check for body element (https://drafts.csswg.org/cssom-view/#potentially-scrollable)
      if (element === document.body) {
        const parentStyle = window.getComputedStyle(element.parentElement);
        if (parentStyle["overflow"] !== "visible" && parentStyle["overflow-y"] !== "visible" &&
            parentStyle["overflow"] !== "clip" && parentStyle["overflow-y"] !== "clip"
        ) {
          return true;
        }
      }
      else return true;
    }
  }
  
  return false;
}
Robbendebiene
fonte
0

Aqui está a minha melhoria: parseInt adicionado. por algum motivo estranho, não funcionou sem ele.

// usage: jQuery('#my_div1').hasVerticalScrollBar();
// Credit: http://stackoverflow.com/questions/4814398/how-can-i-check-if-a-scrollbar-is-visible
(function($) {
    $.fn.hasVerticalScrollBar = function() {
        return this.get(0) ? parseInt( this.get(0).scrollHeight ) > parseInt( this.innerHeight() ) : false;
    };
})(jQuery);
Svetoslav Marinov
fonte
0

Funciona no Chrome , Edge , Firefox e Opera , pelo menos nas versões mais recentes.

Usando JQuery ...

Configure esta função para corrigir o rodapé:

function fixFooterCaller()
{
    const body = $('body');
    const footer = $('body footer');

    return function ()
    {
        // If the scroll bar is visible
        if ($(document).height() > $(window).height())
        {
            // Reset
            footer.css('position', 'inherit');
            // Erase the padding added in the above code
            body.css('padding-bottom', '0');
        }
        // If the scrollbar is NOT visible
        else
        {
            // Make it fixed at the bottom
            footer.css('position', 'fixed');
            // And put a padding to the body as the size of the footer
            // This makes the footer do not cover the content and when
            // it does, this event fix it
            body.css('padding-bottom', footer.outerHeight());
        }
    }
}

Retorna uma função. Feito dessa maneira apenas para definir o corpo e o rodapé uma vez.

E então, defina isso quando o documento estiver pronto.

$(document).ready(function ()
{
    const fixFooter = fixFooterCaller();

    // Put in a timeout call instead of just call the fixFooter function
    // to prevent the page elements needed don't be ready at this time
    setTimeout(fixFooter, 0);
    // The function must be called every time the window is resized
    $(window).resize(fixFooter);
});

Adicione isso ao seu rodapé css:

footer {
    bottom: 0;
}
Mateus Pires
fonte
0

A maioria das respostas apresentadas me aproximou de onde eu precisava estar, mas não chegou lá.

Basicamente, queríamos avaliar se as barras de rolagem ficariam visíveis em uma situação normal, por essa definição, significando que o tamanho do elemento body é maior que a porta de visualização. Esta não foi uma solução apresentada, e é por isso que a estou enviando.

Espero que ajude alguém!

(function($) {
    $.fn.hasScrollBar = function() {
        return this.get(0).scrollHeight > $(window).height();
    }
})(jQuery);

Essencialmente, temos a hasScrollbarfunção, mas retornando se o elemento solicitado for maior que a porta de visualização. Para ver o tamanho da porta, acabamos de usar $(window).height(). Uma comparação rápida com o tamanho do elemento gera os resultados corretos e o comportamento desejável.

Barry Chapman
fonte
0

Encontre um pai do elemento atual que tenha rolagem vertical ou corpo.

$.fn.scrollableParent = function() {
    var $parents = this.parents();

    var $scrollable = $parents.filter(function(idx) {
        return this.scrollHeight > this.offsetHeight && this.offsetWidth !== this.clientWidth;
    }).first();

    if ($scrollable.length === 0) {
        $scrollable = $('html, body');
    }
    return $scrollable;
};

Pode ser usado para rolagem automática para o elemento atual via:

var $scrollable = $elem.scrollableParent();
$scrollable.scrollTop($elem.position().top);
Dmitriy Sintsov
fonte
0

Uma abordagem JavaScript sem estrutura, verifica tanto a vertical quanto a horizontal

 /*
 * hasScrollBars
 * 
 * Checks to see if an element has scrollbars
 * 
 * @returns {object}
 */
Element.prototype.hasScrollBars = function() {
    return {"vertical": this.scrollHeight > this.style.height, "horizontal": this.scrollWidth > this.style.width};
}

Use-o assim

if(document.getElementsByTagName("body")[0].hasScrollBars().vertical){
            alert("vertical");
}

        if(document.getElementsByTagName("body")[0].hasScrollBars().horizontal){
            alert("horizontal");
}
David Clews
fonte