Como faço para definir o deslocamento para ScrollSpy no Bootstrap?

98

Tenho um site com a barra de navegação fixada na parte superior e 3 divs na parte inferior da área de conteúdo principal.

Estou tentando usar scrollspy da estrutura de bootstrap.

Eu tenho sucesso destacando os diferentes títulos no menu quando você rola para além dos divs.

Também o tenho para que, quando você clicar no menu, ele role para a parte correta da página. No entanto, o deslocamento está incorreto (não leva em consideração a barra de navegação, então preciso deslocar em cerca de 40 pixels)

Vejo na página Bootstrap que menciona uma opção de deslocamento, mas não tenho certeza de como usá-lo.

Também não sou o que significa quando diz que você pode usar scrollspy com $('#navbar').scrollspy(), não tenho certeza de onde incluí-lo, então não o fiz e tudo parece estar funcionando (exceto o deslocamento).

Achei que o deslocamento poderia ser data-offset='10'na etiqueta do corpo, mas isso não faz nada para mim.

Tenho a sensação de que isso é algo realmente óbvio e estou simplesmente perdendo isso. Qualquer ajuda?

Meu código é

...
<!-- note: the data-offset doesn't do anything for me -->
<body data-spy="scroll" data-offset="20">
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
    <div class="container">
    <a class="brand" href="#">VIPS</a>
    <ul class="nav">
        <li class="active">
             <a href="#trafficContainer">Traffic</a>
        </li>
        <li class="">
        <a href="#responseContainer">Response Times</a>
        </li>
        <li class="">
        <a href="#cpuContainer">CPU</a>
        </li>
      </ul>
    </div>
</div>
</div>
<div class="container">
<div class="row">
    <div class="span12">
    <div id="trafficContainer" class="graph" style="position: relative;">
    <!-- graph goes here -->
    </div>
    </div>
</div>
<div class="row">
    <div class="span12">
    <div id="responseContainer" class="graph" style="position: relative;">
    <!-- graph goes here -->
    </div>
    </div>
</div>
<div class="row">
    <div class="span12">
    <div id="cpuContainer" class="graph" style="position: relative;">
    <!-- graph goes here -->
    </div>
    </div>
</div>
</div>

...
<script src="assets/js/jquery-1.7.1.min.js"></script>
<script src="assets/js/bootstrap-scrollspy.js"></script>
</body>
</html>
Lango
fonte
Por que você está dando uma posição: relativa aos seus divs? Talvez seja isso que está causando isso. Você pode criar um jsfiddle com seu código para que possamos ver em ação?
periklis

Respostas:

113

Bootstrap usa deslocamento para resolver apenas espionagem, não rolagem. Isso significa que a rolagem para o local adequado depende de você.

Experimente isso, funciona para mim: adicione um manipulador de eventos para os cliques de navegação.

var offset = 80;

$('.navbar li a').click(function(event) {
    event.preventDefault();
    $($(this).attr('href'))[0].scrollIntoView();
    scrollBy(0, -offset);
});

Encontrei aqui: https://github.com/twitter/bootstrap/issues/3316

Kurt UXD
fonte
3
Obrigado pela resposta. Levei um bom tempo para perceber que o deslocamento não afeta a rolagem. Isso significa que qualquer pessoa que use uma tampa fixa precisa ajustar manualmente dessa maneira.
Brian Smith
8
Para atualizar a seção hash do URL de forma adequada, adicione window.location.hash = $(this).attr('href')a esta função.
Stephen M. Harris
2
Obrigado! FWIW, colocar o deslocamento de dados no corpo também é importante, pois o autor da pergunta diz que ele não faz nada, mas é necessário para uma espionagem adequada. Pode ser útil ter uma resposta abrangente.
mrooney
4
Vitória dinâmica: var navOffset = $('#navbar').height();escrollBy(0, -navOffset);
ahnbizcad
78

O truque, como Tim aludiu, é aproveitar as vantagens do preenchimento. Portanto, o problema é que seu navegador sempre deseja rolar sua âncora até o topo exato da janela. se você definir sua âncora onde seu texto realmente começa, ele será obstruído pela barra de menu. Em vez disso, o que você faz é definir sua âncora para o ID do contêiner <section>ou <div>tag, que o nav / navbar usará automaticamente. Em seguida, você deve definir seu padding-toppara o contêiner com a compensação de valor que deseja e o margin-toppara o contêiner como o oposto de padding-top. Agora o seu bloco de contêiner e a âncora começam exatamente no topo da página, mas o conteúdo interno não começa até abaixo da barra de menu.

Se você estiver usando âncoras regulares, poderá realizar a mesma coisa usando a margem superior negativa em seu contêiner, como acima, mas sem preenchimento. Em seguida, você coloca uma <a target="...">âncora regular como o primeiro filho do contêiner. Em seguida, você estiliza o segundo elemento filho com um oposto, mas igual margin-top, mas usando o seletor .container:first-child +.

Isso tudo presume que sua <body>tag já tem sua margem definida para começar abaixo do seu cabeçalho, o que é a norma (caso contrário, a página seria renderizada com o texto ocluído logo de cara).

Aqui está um exemplo disso em ação. Qualquer coisa em sua página com um id pode ser vinculado colocando # the-elements-id no final do seu URL. Se você fizer todas as suas h tags ad dt tags com ids que podem ser vinculados ( como o github faz ) com algum tipo de script que injeta um pequeno link à esquerda deles ; adicionar o seguinte ao seu CSS cuidará do deslocamento da barra de navegação para você:

body {
    margin-top: 40px;/* make room for the nav bar */
}

/* make room for the nav bar */
h1[id],
h2[id],
h3[id],
h4[id],
h5[id],
h6[id],
dt[id]{
    padding-top: 60px;
    margin-top: -40px;
}
acjay
fonte
9
Adorei a combinação de preenchimento superior, margem negativa inferior (no mesmo elemento). Mais fácil do que brincar com o js.
Eystein
Parece a melhor resposta, mas estou um pouco perdida. Qualquer chance de um exemplo.
eddyparkinson
@eddyparkinson, acho que você descobriu agora: Exemplo, quando você tem seus ids nas linhas do bootstrap: .row [id] {padding-top: 60px; margin-top: -40px; } Coloque isso no seu arquivo less: .row [id] {padding-top: 60px; margin-top: -40px; }
Klaaz
O parâmetro de deslocamento padrão do bootstrap não deveria ser para isso? Parece algo óbvio para incluir, e não é necessário recorrer a soluções alternativas como esta.
ahnbizcad de
Isso efetivamente adiciona espaço extra no div que você está almejando? E se você não quiser isso? Um espaçamento extra invisível seria o ideal.
ahnbizcad de
10

Você pode alterar o deslocamento de scrollspy na hora com isto (supondo que você esteja espionando o corpo):

offsetValue = 40;
$('body').data().scrollspy.options.offset = offsetValue;
// force scrollspy to recalculate the offsets to your targets
$('body').data().scrollspy.process();

Isso também funciona se você precisar alterar o valor de deslocamento dinamicamente. (Gosto que o scrollspy mude quando a próxima seção estiver na metade da janela, então preciso alterar o deslocamento durante o redimensionamento da janela.)

cogell
fonte
2
Obrigado por isso - funciona muito bem com um pequeno ajuste para Bootstrap 3.0 $ ('body'). Data () ['bs.scrollspy']. Options.offset = newOffset; // Define o novo deslocamento $ ('body'). Data () ['bs.scrollspy']. Process (); // Força o scrollspy a recalcular os deslocamentos para seus alvos $ ('body'). Scrollspy ('refresh'); // Atualize o scrollspy.
Sam
Descobri que esse método é bom, mas se eu quiser usá-lo para definir o deslocamento desde o início, devido a ter diferentes alturas de menu, o $ ('body'). Data (). Scrollspy.options.offset (ou a variante para a versão 3.0) ainda não foi definida. Existe algo assíncrono sobre scrollspy que estou perdendo?
ness-EE
No Bootstrap 3.1.1 isso deveria ser $('body').data()['bs.scrollspy'].options.offset. Útil para acionar scrollspy no carregamento da página usandowindow.scrollBy(X,Y)
Meredith
8

Se você quiser um deslocamento de 10, você deve colocar isso em sua cabeça:

<script>
  $('#navbar').scrollspy({
    offset: 10
  });
</script>

Sua tag de corpo seria assim:

<body data-spy="scroll">
David Naffis
fonte
6
Isso não compensa o conteúdo, mas sim o nav.
markus de
Isso funcionará para destacar o elemento de navegação correto e não o local para onde o clique leva (que é a pergunta original dos pôsteres). Com isso dito, era isso que eu procurava! Obrigado!
valin077
6

Do problema de bootstrap nº 3316 :

[Isto] destina-se apenas a modificar os cálculos de rolagem - não mudamos o clique da âncora real

Portanto, definir o deslocamento afeta apenas onde o scrollspy pensa que a página foi rolada . Não afeta a rolagem quando você clica em uma tag âncora.

Usar uma barra de navegação fixa significa que o navegador está rolando para o lugar errado. Você pode corrigir isso com JavaScript:

var navOffset = $('.navbar').height();

$('.navbar li a').click(function(event) {
    var href = $(this).attr('href');

    // Don't let the browser scroll, but still update the current address
    // in the browser.
    event.preventDefault();
    window.location.hash = href;

    // Explicitly scroll to where the browser thinks the element
    // is, but apply the offset.
    $(href)[0].scrollIntoView();
    window.scrollBy(0, -navOffset);
});

No entanto, para garantir que scrollspy destaque o elemento atual correto, você ainda precisa definir o deslocamento:

<body class="container" data-spy="scroll" data-target=".navbar" data-offset="70">

Aqui está uma demonstração completa do JSFiddle .


Alternativamente, você pode preencher as tags âncora, conforme sugerido por truques CSS .

a[id]:before { 
  display: block; 
  content: " ";
  // Use the browser inspector to find out the correct value here.
  margin-top: -285px; 
  height: 285px; 
  visibility: hidden; 
}
Wilfred Hughes
fonte
1

Acho que esse deslocamento pode fazer algo com a posição inferior da seção.

Corrigi meu problema - igual ao seu - adicionando

  padding-top: 60px;

na declaração para a seção em bootstrap.css

Espero que ajude!

Tim
fonte
o que é uma "declaração para a seção" Onde você coloca esse código?
ahnbizcad
1

Eu adicionei um .vspaceelemento de altura de 40px segurando a âncora antes de cada um dos meus h1elementos.

<div class="vspace" id="gherkin"></div>
<div class="page-header">
  <h1>Gherkin</h1>
</div>

No CSS:

.vspace { height: 40px;}

Está funcionando muito bem e o espaço não é chocante.

Quentin
fonte
1

Bootstrap 4 (atualização de 2019)

Implementando: Nav fixo, Scrollspy e Smooth Scrolling

Esta é a solução de @ wilfred-hughes acima, ela corrige exatamente o problema de sobreposição com o Bootstrap Fixed Navbar usando CSS ':: before' para preceder um bloco não exibido antes de cada tag de seção. Vantagem: você não acaba com preenchimento desnecessário entre suas seções. Por exemplo, a seção 3 pode ser posicionada verticalmente contra a seção 2 e ainda irá rolar para a posição correta exata no topo da seção 3.

Nota lateral: definir o deslocamento de scrollspy na tag de script não causou nada que acontecesse ($ ('# navbar'). Scrollspy ({offset: 105});) e as tentativas de ajustar o deslocamento no bloco de animação ainda resultaram em desalinhamento pós- animação.

index.html

<section id="section1" class="my-5">
...
<section id="section2" class="my-5">
...
<section id="section3" class="my-5">
...

mais tarde em index.html ...

 <script>
      // Init Scrollspy
      $('body').scrollspy({ target: '#main-nav' });

      // Smooth Scrolling
      $('#main-nav a').on('click', function(event) {
        if (this.hash !== '') {
          event.preventDefault();

          const hash = this.hash;

          $('html, body').animate(
            {
              scrollTop: $(hash).offset().top
            },
            800,
            function() {
              window.location.hash = hash;
            }
          );
        }
      });
    </script>

style.css:

// Solution to Fixed NavBar overlapping content of the section.  Apply to each navigable section.
// adjust the px values to your navbar height
// Source: https://github.com/twbs/bootstrap/issues/1768
#section1::before,
#section2::before,
#section3::before {
  display: block;
  content: ' ';
  margin-top: -105px;
  height: 105px;
  visibility: hidden;
}

body {
  padding-top: 105px;
}

Crédito: @ wilfred-hughes acima e a fonte original do usuário @mnot em https://github.com/twbs/bootstrap/issues/1768

ObjectiveTC
fonte
1
obrigado! ugh, estive lutando com isso por uma hora, LOL!
Nexus
0

Tive problemas com as soluções de acjohnson55 e Kurt UXD. Ambos funcionaram para clicar em um link para o hash, mas o deslocamento scrollspy ainda não estava correto.

Eu encontrei a seguinte solução:

<div class="anchor-outer">
  <div id="anchor">
    <div class="anchor-inner">
      <!-- your content -->
    </div>
  </div>
</div>

E o CSS correspondente:

body {
  padding-top:40px;
}

.anchor-outer {
  margin-top:-40px;
}

.anchor-inner {
  padding-top:40px;
}

@media (max-width: 979px) {
  body {
    padding-top:0px;
  }

  .anchor-outer {
    margin-top:0px;
  }

  .anchor-inner {
    padding-top:0px;
  }
}

@media (max-width: 767px) {
  body {
    padding-top:0px;
  }

  .anchor-outer {
    margin-top:0px;
  }

  .anchor-inner {
    padding-top:0px;
  }
}

Você precisará substituir o preenchimento / margem de 40px pela altura de sua barra de navegação e o id de sua âncora.

Nesta configuração, posso até observar um efeito de definir um valor para o atributo de deslocamento de dados. Se encontrar grandes valores para o deslocamento de dados em torno de 100-200 leva a um comportamento mais esperado (o scrollspy- "switch" acontecerá se o título estiver em torno do meio da tela).

Também descobri que o scrollspy é muito sensível a erros como tags div não fechadas (o que é bastante óbvio), então http://validator.w3.org/check era meu amigo aqui.

Na minha opinião, scrollspy funciona melhor com seções inteiras carregando o ID da âncora, uma vez que os elementos âncora regulares não levam aos efeitos esperados ao rolar para trás (o scrollspy- "switch" eventualmente acontece assim que você atinge o elemento âncora, por exemplo, seu título, mas naquele momento você já rolou sobre o conteúdo que o usuário espera pertencer ao título correspondente).

Schreon
fonte
0

Se você tropeçou nesta questão (como eu fiz) através do Google e ainda está procurando uma maneira de alterar dinamicamente o deslocamento do scrollspy. Agora estou anexando um link para uma solução que funcionou para mim.

Dê uma olhada neste post que fiz em outro tópico . Ele contém um trecho de como você pode alterar programaticamente o valor de deslocamento e reagir dinamicamente às alterações (por exemplo, se a barra de navegação mudar de tamanho).

Você não precisa mexer em consertos CSS, mas sim definir o deslocamento que você precisa atualmente na dependência de certos eventos.

thex
fonte
-1

Você também pode abrir bootstap-offset.jse modificar

$.fn.scrollspy.defaults = {
  offset: 10
}

há.

Adobe
fonte
1
O deslocamento scrollspy não compensa o conteúdo, ele compensa o nav.
markus de
-1

Basta definir:

data-offset="200"

data-offseté por padrão "50" e isso equivale a 10px, então apenas aumente data-offsete seu problema estará resolvido.

Zivanic
fonte