Faire passer le lien d'ancrage à quelques pixels au-dessus de l'endroit où il est lié

124

Je ne suis pas sûr de la meilleure façon de poser / rechercher cette question:

Lorsque vous cliquez sur un lien d'ancrage, cela vous amène à cette section de la page avec la zone liée maintenant en TRÈS HAUT de la page. J'aimerais que le lien d'ancrage m'envoie vers cette partie de la page, mais j'aimerais un peu d'espace en haut. Comme dans, je ne veux pas qu'il m'envoie à la partie liée avec lui au TRÈS HAUT, je voudrais environ 100 pixels d'espace là-bas.

Est-ce que ça a du sens? Est-ce possible?

Édité pour afficher le code - c'est juste une balise d'ancrage:

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

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

Réponses:

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

Cela permettra au navigateur de faire le travail de sauter à l'ancre pour nous, puis nous utiliserons cette position pour décaler.

MODIFIER 1:

Comme l'a souligné @erb, cela ne fonctionne que si vous êtes sur la page pendant que le hachage est modifié. La saisie de la page avec un #somethingdéjà dans l'URL ne fonctionne pas avec le code ci-dessus. Voici une autre version pour gérer cela:

// 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).

REMARQUE: pour utiliser jQuery, vous pouvez simplement remplacer window.addEventListenerpar $(window).ondans les exemples. Merci @Neon.

MODIFIER 2:

Comme quelques-uns l'ont souligné, l'opération ci-dessus échouera si vous cliquez sur le même lien d'ancrage deux fois ou plus de suite car il n'y a pas d' hashchangeévénement pour forcer le décalage.

Cette solution est une version très légèrement modifiée de la suggestion de @Mave et utilise des sélecteurs jQuery pour plus de simplicité

// 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);

JSFiddle pour cet exemple est ici

Eric Olson
la source
1
Cela ne fonctionne que lorsque l'utilisateur est déjà sur la page, ce n'est pas le cas, par exemple, lorsque l'utilisateur visite à example.com/#anchorpartir de example.com/about/.
erb
7
Merci @erb. La question du PO n'exigeait pas ce cas, mais pour aider les autres, j'ai ajouté une autre version de l'implémentation.
Eric Olson
2
Il manque toujours un détail mineur. Si vous cliquez sur une ancre, puis faites défiler vers le haut puis cliquez à nouveau sur la même ancre, il n'y a pas de changement de hachage donc il n'utilisera pas le décalage. Des idées?
Teo Maragakis le
Vous pourriez y aller $('a[href^="#"]').on('click', function() { offsetAnchor(); });. De cette façon, si le hrefd'un aélément commence par a #, vous appelez la même fonction.
Mave
2
Pas besoin de jQuery - il suffit de le remplacer $(window).on("hashchange",parwindow.addEventListener("hashchange",
Neon
90

En travaillant uniquement avec css, vous pouvez ajouter un remplissage à l'élément ancré (comme dans une solution ci-dessus) Pour éviter les espaces inutiles, vous pouvez ajouter une marge négative de la même hauteur:

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

Je ne suis pas sûr que ce soit la meilleure solution dans tous les cas, mais cela fonctionne très bien pour moi.

user3364768
la source
3
s'il y a un lien à moins de 50 pixels au-dessus, il peut être masqué et rendu non cliquable je crois
Andrew
@Andrew Cela ne semble pas être un problème sur IE10 ou Chrome.
Sinister Beard
11
vous pouvez faire de même en utilisant la position: relativepropriété css. Donc, ce serait commeposition: relative; top: -50px;
Aleks Dorohovich
@AleksDorohovich - Vous pouvez publier cela comme une réponse distincte car elle est unique à celles existantes.
BSMP
Ma page est devenue sauvage ... * _ *
64

Encore meilleure solution:

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

Positionnez simplement l' <a>étiquette avec un positionnement absolu à l'intérieur d'un objet relativement positionné.

Fonctionne lors de l'entrée dans la page ou via un changement de hachage dans la page.

Thomas
la source
4
Je préfère votre solution car elle n'utilise pas de javascript. Merci!
Rosario Russo
1
Je ne comprends pas cette réponse. Vous référez-vous ce lien à l'objet ou à l'objet lui-même?
CZorio
3
Très utile avec les barres de navigation à dessus fixe bootstrap
Outside_Box
1
@CZorio Si j'ai bien compris, il crée (ingénieusement) un élément d'ancrage (peut être autre chose, comme "span" au lieu de "a") et le positionne relativement en dessous, de sorte que l'ancre href="#anchor"qui s'y trouve pointe là, au lieu de pointer <p>directement vers l' élément. Mais un avertissement: utilisez idplutôt que namedans HTML5, voir: stackoverflow.com/questions/484719/html-anchors-with-name-or-id
flen
1
Vraiment une meilleure solution :)
Bobby Axe
23

Voici la réponse 2020 à cela:

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

Parce que c'est largement soutenu !

user127091
la source
1
Évalué parce que ce serait bien, ce n'est PAS largement pris en charge. Ni opera mobile / mini, ni navigateur safari / ios, même le premier Edge ne le prend totalement en charge.
Beda Schmid le
1
Un peu moins de 90% de tous les navigateurs (y compris iOS avec scroll-snap-margin-top) devrait être suffisant pour désactiver une solution javascript, surtout si l'impact utilisateur est simplement de devoir faire défiler. Mais bon, rejetons cette approche avant qu'elle ne nuise à quelqu'un.
user127091
Une réponse si simple et efficace. Atteindra bientôt 100% du support (pour les navigateurs modernes).
Leonel Becerra le
17

Meilleure solution

<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>
Никита Андрейчук
la source
8
Pourquoi est-ce la meilleure solution? En outre, c'est beaucoup plus utile si vous donnez un certain niveau d'explication / de raisonnement avec une réponse.
Phill Healey
Comme vous ajoutez du HTML pour créer une ancre de toute façon, l'ajout d'un span n'est pas très différent. Ensuite, en ajoutant simplement un peu de CSS, vous parvenez à résoudre ce problème sans gagner de bogues; comme certaines autres solutions peuvent le faire.
Sondre le
11

Cela fonctionnera sans jQuery et au chargement de la page.

(function() {
    if (document.location.hash) {
        setTimeout(function() {
            window.scrollTo(window.scrollX, window.scrollY - 100);
        }, 10);
    }
})();
MK
la source
1
Fonctionne pour moi, et a l'avantage par rapport à la méthode css qu'elle s'applique à tous les emplacements de hachage sur une page, de sorte que vous n'ayez pas à écrire un style avec chaque hachage inclus et n'oubliez pas de modifier le style lorsque vous ajoutez un autre para avec un nouveau hachage. Cela permet également de styliser la cible avec une couleur d'arrière-plan sans l'extension de 100 pixels (ou autre) ci-dessus, comme dans la réponse de @ user3364768, bien qu'il existe un moyen de contourner cela.
David
4

Pour créer un lien vers un élément, puis `` positionner '' cet élément à une distance arbitraire du haut de la page, en utilisant du CSS pur, vous devrez utiliser padding-top, de cette façon, l'élément est toujours positionné en haut de la fenêtre, mais il apparaît , visiblement, positionné à une certaine distance du haut du port de vue, par exemple:

<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;
}

Démo de JS Fiddle .

Pour utiliser une approche JavaScript simple:

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

window.addEventListener('hashchange', addMargin);

Démo de JS Fiddle .

David dit de réintégrer Monica
la source
1
Le seul problème avec cela est qu'il pousse maintenant ce div réel plus bas dans la page. Idéalement, le lien d'ancrage descendrait simplement de 100 pixels de moins qu'il ne le ferait normalement. Je suis prêt à utiliser JS ou jQuery si nécessaire pour y parvenir.
damon
3
Cela ne résout pas le problème, cela ajoute simplement un espace blanc avant l'élément.
XCS
user1925805: consultez la réponse mise à jour pour une solution JavaScript. @Cristy: Je sais , j'ai explicitement déclaré que dans la réponse elle-même: l'élément est toujours positionné en haut de la fenêtre, mais il semble, visiblement, être positionné à une certaine distance du haut .
David dit de réintégrer Monica le
Meilleure solution propre avec votre JS. Merci cela fonctionne très bien pour moi!
DanielaB67
Cela ne fonctionne que les changements de hachage se sont produits dans la page. Mais l'utilisation a été redirigée avec une pièce d'identité, alors cela ne fonctionne pas
Rohan Khude
4

Cela devrait fonctionner:

    $(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;
    });
});

Changez simplement le .top-49 en fonction de votre lien d'ancrage.

dMehta
la source
3

Si vous utilisez des noms d'ancrage explicites tels que,

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

puis en css, vous pouvez simplement définir

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

Cela fonctionnera tant que vos balises d'ancrage HREF ne spécifient pas également un attribut NAME

Mespr
la source
Merci. C'est la seule solution de cette liste qui a fonctionné pour moi.
MikeyBunny
3

La réponse d'Eric est excellente, mais vous n'avez vraiment pas besoin de ce délai. Si vous utilisez jQuery, vous pouvez simplement attendre que la page se charge. Je suggère donc de changer le code en:

// 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();
});

Cela nous permet également de nous débarrasser de ce facteur arbitraire.

rppc
la source
3

essayez ce code, il a déjà une animation fluide lorsque vous cliquez sur le lien.

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

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

La solution la plus simple:

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
la source
1

Une variante de la solution de Thomas: élément CSS > sélecteurs d' élément peut être pratique ici:

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>

Un clic sur le lien déplacera la position de défilement à 100px ci-dessus là où l'élément avec une classe de paddedAnchorest positionné.

Pris en charge dans les navigateurs non IE et dans IE à partir de la version 9. Pour une prise en charge sur IE 7 et 8, a <!DOCTYPE>doit être déclaré.

HondaGuy
la source
1

Je viens de trouver une solution facile pour moi-même. Avait une marge supérieure de 15 px

HTML

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

CSS

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

En utilisant uniquement css et n'ayant aucun problème avec le contenu couvert et non cliquable auparavant (le point en est le pointeur-événements: aucun):

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
la source
1

Mettez cela avec style:

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

et utilisez cette classe dans une divbalise séparée avant les liens, exemple:

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

ou utilisez ce code php pour la balise echo link:

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

Je sais que c'est un peu tard, mais j'ai trouvé quelque chose de très important à mettre dans votre code si vous utilisez Scrollspy de Bootstrap. ( http://getbootstrap.com/javascript/#scrollspy )

Cela m'a rendu fou pendant des heures.

Le décalage de l'espion de défilement DOIT correspondre à window.scrollY sinon vous courrez le risque de:

  1. Obtenir un effet de scintillement étrange lors du défilement
  2. Vous constaterez que lorsque vous cliquez sur les ancres, vous atterrissez dans cette section, mais l'espion de défilement supposera que vous êtes une section au-dessus.

 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);
});

viande de bœuf
la source
Seulement js dans l'extrait? Cela ne fonctionnera pas quoi qu'il
0

Basé sur la solution @Eric Olson , modifiez un peu pour inclure l'élément d'ancrage que je veux aller spécifiquement

// 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
la source
0

J'ai juste le même problème. J'ai un nav posé pixed et je veux que l'angkor démarre sous le nav. La solution window.addEventListener...ne fonctionne pas pour moi parce que j'ai configuré ma page pour scroll-behavior:smoothqu'elle définisse le décalage au lieu de faire défiler vers l'angkor. le setTimeout()travail si le temps est écoulé pour faire défiler jusqu'à la fin mais il ne semble toujours pas bon. ma solution a donc été d'ajouter un div absolu posé dans angkor, avec height:[the height of the nav]et bottom:100%. dans ce cas, ce div se termine en haut de l'élément angkor, et commence à la position où vous voulez faire défiler l'angkor. maintenant tout ce que je fais est de définir le lien angkor vers ce div absolu et le travail est fait :)

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
la source
0

J'étais confronté au problème similaire et j'ai résolu en utilisant le code suivant

$(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
la source
-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
la source
4
C'est essentiellement la même réponse que celle-ci . Si vous postez des réponses à une ancienne question, essayez d'ajouter quelque chose de nouveau. Par exemple, vous pourriez décrire pourquoi cela fonctionne.
Artjom B.
1
Je dirais que toute réponse devrait avoir une sorte de description de la raison pour laquelle cela fonctionne. ;-)
Phill Healey