Faça o link da âncora ir alguns pixels acima de onde está vinculado

123

Não tenho certeza da melhor maneira de fazer / pesquisar esta pergunta:

Quando você clica em um link de âncora, ele é direcionado para a seção da página com a área vinculada agora, no MUITO TOPO da página. Gostaria que o link da âncora me enviasse para essa parte da página, mas gostaria de algum espaço na parte superior. Como não quero que ele me envie para a parte vinculada a ele no MUITO TOP, eu gostaria de ter mais ou menos 100 pixels de espaço lá.

Isso faz sentido? Isso é possível?

Editado para mostrar o código - é apenas uma tag de âncora:

<a href="#anchor">Click me!</a>

<p id="anchor">I should be 100px below where I currently am!</p>
Damon
fonte

Respostas:

156
window.addEventListener("hashchange", function () {
    window.scrollTo(window.scrollX, window.scrollY - 100);
});

Isso permitirá que o navegador faça o trabalho de pular para a âncora para nós e, em seguida, usaremos essa posição para compensar.

EDIT 1:

Como foi apontado por @erb, isso só funciona se você estiver na página enquanto o hash for alterado. A entrada da página com um #somethingURL já não funciona com o código acima. Aqui está outra versão para lidar com isso:

// The function actually applying the offset
function offsetAnchor() {
    if(location.hash.length !== 0) {
        window.scrollTo(window.scrollX, window.scrollY - 100);
    }
}

// This will capture hash changes while on the page
window.addEventListener("hashchange", offsetAnchor);

// This is here so that when you enter the page with a hash,
// it can provide the offset in that case too. Having a timeout
// seems necessary to allow the browser to jump to the anchor first.
window.setTimeout(offsetAnchor, 1); // The delay of 1 is arbitrary and may not always work right (although it did in my testing).

NOTA: Para usar o jQuery, você pode simplesmente substituir window.addEventListenercom $(window).onnos exemplos. Obrigado @Neon.

EDIT 2:

Conforme apontado por alguns, o acima falhará se você clicar no mesmo link de ancoragem duas ou mais vezes seguidas porque não há hashchangeevento para forçar o deslocamento.

Esta solução é uma versão ligeiramente modificada da sugestão do @Mave e usa os seletores jQuery para simplificar

// The function actually applying the offset
function offsetAnchor() {
  if (location.hash.length !== 0) {
    window.scrollTo(window.scrollX, window.scrollY - 100);
  }
}

// Captures click events of all <a> elements with href starting with #
$(document).on('click', 'a[href^="#"]', function(event) {
  // Click events are captured before hashchanges. Timeout
  // causes offsetAnchor to be called after the page jump.
  window.setTimeout(function() {
    offsetAnchor();
  }, 0);
});

// Set the offset when entering page with hash present in the url
window.setTimeout(offsetAnchor, 0);

O JSFiddle para este exemplo está aqui

Eric Olson
fonte
1
Isso só funciona quando o usuário já está na página, isso não acontece, por exemplo, quando o usuário visita example.com/#anchorde example.com/about/.
erb
7
Obrigado @erb. A pergunta do OP não exigia esse caso, mas para ajudar outras pessoas, adicionei outra versão da implementação.
11114 Eric Ericson
2
Ainda falta um pequeno detalhe. Se você clicar em uma âncora e, em seguida, role para cima e clique na mesma âncora novamente, não há alteração de hash, portanto não usará o deslocamento. Ideias?
Teo Maragakis
Você poderia ir $('a[href^="#"]').on('click', function() { offsetAnchor(); });. Dessa forma, se o hrefde um aelemento começar com a #, você chamará a mesma função.
Mave
2
Não há necessidade de jQuery - basta substituir $(window).on("hashchange",comwindow.addEventListener("hashchange",
Neon
89

Trabalhando apenas com css, você pode adicionar um preenchimento ao elemento ancorado (como na solução acima) Para evitar espaços em branco desnecessários, você pode adicionar uma margem negativa da mesma altura:

#anchor {
    padding-top: 50px;
    margin-top: -50px;
}

Não tenho certeza se esta é a melhor solução em qualquer caso, mas funciona bem para mim.

user3364768
fonte
3
se há um link dentro 50px acima dela, pode ser coberto e fez unclickable Eu acredito
Andrew
@ Andrew Isso não parece ser um problema no IE10 ou Chrome.
Barba Sinistra
11
você pode fazer o mesmo usando a position: relativepropriedade css. Então, seria comoposition: relative; top: -50px;
Aleks Dorohovich
@AleksDorohovich - Você pode postar isso como uma resposta separada, pois é exclusiva das existentes.
BSMP 15/10/2015
Minha página foi à loucura ... * _ *
64

Solução ainda melhor:

<p style="position:relative;">
    <a name="anchor" style="position:absolute; top:-100px;"></a>
    I should be 100px below where I currently am!
</p>

Apenas posicione a <a>etiqueta com posicionamento absoluto dentro de um objeto relativamente posicionado.

Funciona ao entrar na página ou através de uma alteração de hash na página.

Thomas
fonte
4
Prefiro sua solução porque ela não usa javascript. Obrigado!
Rosario Russo
1
Eu não entendo essa resposta. Você está referenciando esse link ao objeto ou ao próprio objeto?
CZorio 27/05
3
Muito útil com Bootstrap-top fixo navbars
Outside_Box
1
@CZorio Se eu entendi direito, é (engenhosamente) criar um elemento de âncora (poderia ser outra coisa, como "span" em vez de "a") e posicioná-lo relativamente abaixo, para que a âncora href="#anchor"nele aponte para lá, em vez de apontar <p>diretamente para o elemento. Mas um aviso: utilização idem vez de nameem HTML5, consulte: stackoverflow.com/questions/484719/html-anchors-with-name-or-id
Flen
1
Verdadeiramente uma solução melhor :)
Bobby Axe
22

Aqui está a resposta para 2020 para isso:

#anchor {
  scroll-margin-top: 100px;
}

Porque é amplamente suportado !

user127091
fonte
1
Diminuiu a votação porque, embora isso fosse bom, NÃO é amplamente suportado. Nem o Opera Mobile / Mini, nem o Safari Safari / iOS, nem mesmo o primeiro Edge suporta isso totalmente.
Beda Schmid
1
Um pouco menos de 90% de todos os navegadores (incluindo o iOS com scroll-snap-margin-top) deve ser suficiente para optar por não usar uma solução javascript, especialmente se o impacto do usuário for apenas para rolar. Mas, ei, vamos rebaixar essa abordagem antes que ela prejudique alguém.
user127091 25/06
Uma resposta tão simples e funcional. Atingirá 100% do suporte (para navegadores modernos) em breve.
Leonel Becerra
17

Melhor solução

<span class="anchor" id="section1"></span>
<div class="section"></div>

<span class="anchor" id="section2"></span>
<div class="section"></div>

<span class="anchor" id="section3"></span>
<div class="section"></div>

<style>
.anchor{
  display: block;
  height: 115px; /*same height as header*/
  margin-top: -115px; /*same height as header*/
  visibility: hidden;
}
</style>
Никита Андрейчук
fonte
8
Por que essa é a melhor solução? Além disso, é muito mais útil se você der algum nível de explicação / raciocínio com uma resposta.
Phill Healey
Como você está adicionando HTML para criar uma âncora de qualquer maneira, adicionar uma extensão não é muito diferente. Então, apenas adicionando um pouco de CSS, você consegue resolver esse problema sem obter nenhum erro; como algumas outras soluções podem fazer.
Sondre
11

Isso funcionará sem jQuery e no carregamento da página.

(function() {
    if (document.location.hash) {
        setTimeout(function() {
            window.scrollTo(window.scrollX, window.scrollY - 100);
        }, 10);
    }
})();
MK
fonte
1
Funciona para mim e tem a vantagem sobre o método css que se aplica a todos os locais de hash em uma página, para que você não precise escrever um estilo com cada hash incluído e lembre-se de modificar o estilo ao adicionar outro parágrafo com um novo hash. Ele também permite estilizar o alvo com uma cor de plano de fundo sem essa extensão de 100px (ou o que seja) acima, como na resposta de @ user3364768, embora exista uma maneira de contornar isso.
David
4

Para vincular a um elemento e, em seguida, 'posicionar' esse elemento a uma distância arbitrária da parte superior da página, usando CSS puro, você precisaria usar padding-top, dessa forma, o elemento ainda está posicionado na parte superior da janela, mas parece , visivelmente, estar posicionado a alguma distância do topo da porta de visualização, por exemplo:

<a href="#link1">Link one</a>
<a href="#link2">Link two</a>

<div id="link1">
    The first.
</div>

<div id="link2">
    The second.
</div>

CSS:

div {
    /* just to force height, and window-scrolling to get to the elements.
       Irrelevant to the demo, really */
    margin-top: 1000px;
    height: 1000px;
}

#link2 {
    /* places the contents of the element 100px from the top of the view-port */
    padding-top: 100px;
}

Demonstração JS Fiddle .

Para usar uma abordagem simples de JavaScript:

function addMargin() {
    window.scrollTo(0, window.pageYOffset - 100);
}

window.addEventListener('hashchange', addMargin);

Demonstração JS Fiddle .

David diz para restabelecer Monica
fonte
1
O único problema com isso é que agora empurra essa div real mais abaixo na página. Idealmente, o link da âncora diminuiria 100px a menos do que normalmente. Estou disposto a usar JS ou jQuery, se necessário, para conseguir isso.
Damon '
3
Isso não resolve o problema, simplesmente adiciona espaço em branco antes do elemento.
XCS
user1925805: veja a resposta atualizada para uma solução JavaScript. @ Christy: Eu sei , afirmei explicitamente que na resposta em si: o elemento ainda está posicionado no topo da janela, mas parece, visivelmente, estar posicionado a alguma distância do topo .
David diz que restabelece Monica
Solução melhor e limpa com seu JS. Obrigado, funciona muito bem para mim!
DanielaB67
Isso só funciona com alterações de hash dentro da página. Mas o uso foi redirecionada com ID, então isto não funciona
Rohan Khude
4

Isso deve funcionar:

    $(document).ready(function () {
    $('a').on('click', function (e) {
        // e.preventDefault();

        var target = this.hash,
            $target = $(target);

       $('html, body').stop().animate({
        'scrollTop': $target.offset().top-49
    }, 900, 'swing', function () {
    });

        console.log(window.location);

        return false;
    });
});

Basta alterar o .top-49 para o que se encaixa no seu link de ancoragem.

dMehta
fonte
3

Se você usar nomes de âncora explícitos, como,

<a name="sectionLink"></a>
<h1>Section<h1>

então em css você pode simplesmente definir

A[name] {
    padding-top:100px;
}

Isso funcionará desde que suas tags âncora HREF também não especifiquem um atributo NAME

Mespr
fonte
Obrigado. Esta é a única solução desta lista que funcionou para mim.
MikeyBunny
3

A resposta de Eric é ótima, mas você realmente não precisa desse tempo limite. Se você estiver usando o jQuery, aguarde o carregamento da página. Então, eu sugiro alterar o código para:

// The function actually applying the offset
function offsetAnchor() {
    if (location.hash.length !== 0) {
        window.scrollTo(window.scrollX, window.scrollY - 100);
    }
}

// This will capture hash changes while on the page
$(window).on("hashchange", function () {
    offsetAnchor();
});

// Let the page finish loading.
$(document).ready(function() {
    offsetAnchor();
});

Isso também nos livra desse fator arbitrário.

rppc
fonte
3

tente esse código, ele já tem uma animação suave quando clicou no link.

$(document).on('click', 'a[href^="#"]', function (event) {
    event.preventDefault();

    $('html, body').animate({
        scrollTop: $($.attr(this, 'href')).offset().top - 100
    }, 500);
});
Maricar Onggon
fonte
1

A solução mais fácil:

CSS

#link {
    top:-120px; /* -(some pixels above) */
    position:relative;
    z-index:5;
}

HTML

<body>
    <a href="#link">Link</a>
    <div>
        <div id="link"></div> /*this div should placed inside a target div in the page*/
        text
        text
        text
    <div>
</body>
Edalat Mojaveri
fonte
1

Uma variante da solução de Thomas: os elementos CSS > seletores de elementos podem ser úteis aqui:

CSS

.paddedAnchor{
  position: relative;
}
.paddedAnchor > a{
  position: absolute;
  top: -100px;
}

HTML

<a href="#myAnchor">Click Me!</a>

<span class="paddedAnchor"><a name="myAnchor"></a></span>

Um clique no link moverá a posição de rolagem para 100px acima de onde o elemento com uma classe de paddedAnchorestiver posicionado.

Suportado em navegadores não-IE e no IE da versão 9. Para suporte no IE 7 e 8, um <!DOCTYPE>deve ser declarado.

HondaGuy
fonte
1

Acabei de encontrar uma solução fácil para mim. Tinha uma margem superior de 15px

HTML

<h2 id="Anchor">Anchor</h2>

CSS

h2{margin-top:-60px; padding-top:75px;}
Gero
fonte
1

Usando apenas css e sem problemas com conteúdo coberto e não clicável antes (o ponto disso são os eventos-ponteiro: nenhum):

CSS

.anchored::before {
    content: '';
    display: block;
    position: relative;
    width: 0;
    height: 100px;
    margin-top: -100px;
}

HTML

<a href="#anchor">Click me!</a>
<div style="pointer-events:none;">
<p id="anchor" class="anchored">I should be 100px below where I currently am!</p>
</div>
dantefff
fonte
1

Coloque isso em grande estilo:

.hash_link_tag{margin-top: -50px; position: absolute;}

e use essa classe em uma divtag separada antes dos links, exemplo:

<div class="hash_link_tag" id="link1"></div> 
<a href="#link1">Link1</a>

ou use este código php para tag de link de eco:

function HashLinkTag($id)
{
    echo "<div id='$id' class='hash_link_tag'></div>";
}
MohsenB
fonte
0

Sei que é um pouco tarde, mas achei algo muito importante para inserir no seu código se você estiver usando o Scrollspy do Bootstrap. ( http://getbootstrap.com/javascript/#scrollspy )

Isso estava me deixando louco por horas.

A compensação para o espião de rolagem DEVE corresponder ao window.scrollY, caso contrário você corre o risco de:

  1. Obter um efeito estranho de cintilação ao rolar
  2. Você verá que, quando clicar em âncoras, irá pousar nessa seção, mas o scroll spy assumirá que você é uma seção acima dela.

 var body = $('body');
    body.scrollspy({
        'target': '#nav',
        'offset': 100 //this must match the window.scrollY below or you'll have a bad time mmkay
});

$(window).on("hashchange", function () {
        window.scrollTo(window.scrollX, window.scrollY - 100);
});

beefsupreme
fonte
Apenas js no trecho? Não vai funcionar, não importa o que
0

Com base na solução @Eric Olson , modifique um pouco para incluir o elemento âncora que eu quero ir especificamente

// Function that actually set offset
function offsetAnchor(e) {
    // location.hash.length different to 0 to ignore empty anchor (domain.me/page#)
    if (location.hash.length !== 0) {
        // Get the Y position of the element you want to go and place an offset
        window.scrollTo(0, $(e.target.hash).position().top - 150);
    }
}

// Catch the event with a time out to perform the offset function properly
$(document).on('click', 'a[href^="#"]', function (e) {
    window.setTimeout(function () {
        // Send event to get the target id later
        offsetAnchor(e);
    }, 10);
});
Carlos A. Ortiz
fonte
0

Eu apenas tenho o mesmo problema. Eu tenho uma nav postada e quero que o angkor comece sob a nav. A solução window.addEventListener...não funciona para mim, porque eu configurei minha página para scroll-behavior:smoothque ela defina o deslocamento em vez de rolar para o angkor. o setTimeout()trabalho, se houver tempo suficiente para rolar até o final, mas ainda assim não parecer bom. então minha solução foi adicionar uma div absoluta postulada no angkor, com height:[the height of the nav]e bottom:100%. nesse caso, essa div terminou na parte superior do elemento angkor e comece na posição em que você deseja mover o angkor. agora tudo o que eu faço é definir o link angkor para esta div absoluta e o wor feito :)

html,body{
    margin:0;
    padding:0;
    scroll-behavior:smooth;
}

nav {
    height:30px;
    width:100%;
    font-size:20pt;
    text-align:center;
    color:white;
    background-color:black;
    position:relative;
}

#my_nav{
    position:fixed;
    z-index:3;
}
#fixer_nav{
    position:static;
}

#angkor{
    position:absolute;
    bottom:100%;
    height:30px;
}
<nav id="my_nav"><a href="#angkor">fixed position nav<a/></nav>
<nav id="fixer_nav"></nav>

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque nec lacus vel eros rutrum volutpat. Cras ultrices enim sit amet odio dictum, eget consectetur mi pellentesque. Sed mollis gravida nulla, eu euismod turpis efficitur id. Integer pretium posuere fringilla. Aenean laoreet, augue non pharetra elementum, lectus massa congue orci, a imperdiet neque enim ut dui. Praesent commodo orci bibendum leo suscipit viverra. Nunc fermentum semper eleifend. Pellentesque suscipit nulla aliquet, egestas lectus sed, egestas dui. Vivamus scelerisque maximus nibh, ac dignissim nunc tempor a. Praesent facilisis non lacus et aliquam. Proin ultricies lacus vitae nibh ullamcorper gravida. Proin elit arcu, convallis eget posuere quis, placerat id augue. Fusce ex risus, tempus nec orci vitae, feugiat faucibus quam. Integer risus metus, ornare et rhoncus vitae, accumsan a urna.
</p>

<nav><div id="angkor"></div>The angkor</nav>

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque nec lacus vel eros rutrum volutpat. Cras ultrices enim sit amet odio dictum, eget consectetur mi pellentesque. Sed mollis gravida nulla, eu euismod turpis efficitur id. Integer pretium posuere fringilla. Aenean laoreet, augue non pharetra elementum, lectus massa congue orci, a imperdiet neque enim ut dui. Praesent commodo orci bibendum leo suscipit viverra. Nunc fermentum semper eleifend. Pellentesque suscipit nulla aliquet, egestas lectus sed, egestas dui. Vivamus scelerisque maximus nibh, ac dignissim nunc tempor a. Praesent facilisis non lacus et aliquam. Proin ultricies lacus vitae nibh ullamcorper gravida. Proin elit arcu, convallis eget posuere quis, placerat id augue. Fusce ex risus, tempus nec orci vitae, feugiat faucibus quam. Integer risus metus, ornare et rhoncus vitae, accumsan a urna.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque nec lacus vel eros rutrum volutpat. Cras ultrices enim sit amet odio dictum, eget consectetur mi pellentesque. Sed mollis gravida nulla, eu euismod turpis efficitur id. Integer pretium posuere fringilla. Aenean laoreet, augue non pharetra elementum, lectus massa congue orci, a imperdiet neque enim ut dui. Praesent commodo orci bibendum leo suscipit viverra. Nunc fermentum semper eleifend. Pellentesque suscipit nulla aliquet, egestas lectus sed, egestas dui. Vivamus scelerisque maximus nibh, ac dignissim nunc tempor a. Praesent facilisis non lacus et aliquam. Proin ultricies lacus vitae nibh ullamcorper gravida. Proin elit arcu, convallis eget posuere quis, placerat id augue. Fusce ex risus, tempus nec orci vitae, feugiat faucibus quam. Integer risus metus, ornare et rhoncus vitae, accumsan a urna.

</p>

Yosef Tukachinsky
fonte
0

eu estava enfrentando o problema semelhante e resolvi usando o código a seguir

$(document).on('click', 'a.page-scroll', function(event) {
        var $anchor = $(this);
        var desiredHeight = $(window).height() - 577;
        $('html, body').stop().animate({
            scrollTop: $($anchor.attr('href')).offset().top - desiredHeight
        }, 1500, 'easeInOutExpo');
        event.preventDefault();
    });
Umair Mehmood
fonte
-1
<a href="#anchor">Click me!</a>

<div style="margin-top: -100px; padding-top: 100px;" id="anchor"></div>
<p>I should be 100px below where I currently am!</p>
Sportcanon
fonte
4
Essa é essencialmente a mesma resposta que esta . Se você postar respostas à pergunta antiga, tente adicionar algo novo. Por exemplo, você pode descrever por que isso funciona.
precisa
1
Eu diria que qualquer resposta deve ter algum tipo de descrição do porquê funciona. ;-)
Phill Healey