J'essaie de nettoyer le fonctionnement de mes ancres. J'ai un en-tête qui est fixé en haut de la page. Ainsi, lorsque vous créez un lien vers une ancre située ailleurs dans la page, la page saute de manière à ce que l'ancre se trouve en haut de la page, laissant le contenu derrière l'en-tête fixe (j'espère). ça a du sens). J'ai besoin d'un moyen de compenser l'ancre par le 25px de la hauteur de l'en-tête. Je préférerais HTML ou CSS, mais javascript serait également acceptable.
Vous pouvez simplement utiliser CSS sans javascript.
Donnez à votre ancre un cours:
<a class="anchor" id="top"></a>
Vous pouvez ensuite positionner l'ancre avec un décalage supérieur ou inférieur à l'endroit où elle apparaît réellement sur la page, en en faisant un élément de bloc et en le positionnant de manière relative. -250px placera l'ancre jusqu'à 250px
a.anchor {
display: block;
position: relative;
top: -250px;
visibility: hidden;
}
J'ai trouvé cette solution:
<a name="myanchor">
<h1 style="padding-top: 40px; margin-top: -40px;">My anchor</h1>
</a>
Cela ne crée aucune lacune dans le contenu et les liens d'ancrage fonctionnent vraiment bien.
Comme il s’agit d’un problème de présentation, une solution CSS pure serait idéale. Cependant, cette question a été posée en 2012 et, bien que des solutions de positionnement relatif/marge négative aient été suggérées, ces approches semblent plutôt compliquées, créent des problèmes de flux et ne peuvent pas répondre de manière dynamique aux modifications apportées au DOM/viewport.
Dans cet esprit, j'estime que l'utilisation de JavaScript est toujours (février 2017) la meilleure approche. Vous trouverez ci-dessous une solution Vanilla-JS qui répondra à la fois aux clics d’ancrage et à la résolution du hachage de page au chargement (Voir JSFiddle) . Modifiez la méthode .getFixedOffset()
si des calculs dynamiques sont requis. Si vous utilisez jQuery, voici une solution modifiée avec une meilleure délégation d'événements et un défilement régulier .
(function(document, history, location) {
var HISTORY_SUPPORT = !!(history && history.pushState);
var anchorScrolls = {
ANCHOR_REGEX: /^#[^ ]+$/,
OFFSET_HEIGHT_PX: 50,
/**
* Establish events, and fix initial scroll position if a hash is provided.
*/
init: function() {
this.scrollToCurrent();
window.addEventListener('hashchange', this.scrollToCurrent.bind(this));
document.body.addEventListener('click', this.delegateAnchors.bind(this));
},
/**
* Return the offset amount to deduct from the normal scroll position.
* Modify as appropriate to allow for dynamic calculations
*/
getFixedOffset: function() {
return this.OFFSET_HEIGHT_PX;
},
/**
* If the provided href is an anchor which resolves to an element on the
* page, scroll to it.
* @param {String} href
* @return {Boolean} - Was the href an anchor.
*/
scrollIfAnchor: function(href, pushToHistory) {
var match, rect, anchorOffset;
if(!this.ANCHOR_REGEX.test(href)) {
return false;
}
match = document.getElementById(href.slice(1));
if(match) {
rect = match.getBoundingClientRect();
anchorOffset = window.pageYOffset + rect.top - this.getFixedOffset();
window.scrollTo(window.pageXOffset, anchorOffset);
// Add the state to history as-per normal anchor links
if(HISTORY_SUPPORT && pushToHistory) {
history.pushState({}, document.title, location.pathname + href);
}
}
return !!match;
},
/**
* Attempt to scroll to the current location's hash.
*/
scrollToCurrent: function() {
this.scrollIfAnchor(window.location.hash);
},
/**
* If the click event's target was an anchor, fix the scroll position.
*/
delegateAnchors: function(e) {
var elem = e.target;
if(
elem.nodeName === 'A' &&
this.scrollIfAnchor(elem.getAttribute('href'), true)
) {
e.preventDefault();
}
}
};
window.addEventListener(
'DOMContentLoaded', anchorScrolls.init.bind(anchorScrolls)
);
})(window.document, window.history, window.location);
Je cherchais une solution à cela aussi. Dans mon cas, c'était assez facile.
J'ai un menu de liste avec tous les liens:
<ul>
<li><a href="#one">one</a></li>
<li><a href="#two">two</a></li>
<li><a href="#three">three</a></li>
<li><a href="#four">four</a></li>
</ul>
Et ci-dessous les rubriques où il devrait aller.
<h3>one</h3>
<p>text here</p>
<h3>two</h3>
<p>text here</p>
<h3>three</h3>
<p>text here</p>
<h3>four</h3>
<p>text here</p>
Maintenant, parce que j'ai un menu fixe en haut de ma page, je ne peux pas simplement le faire aller à mon tag car ce serait derrière le menu.
Au lieu de cela, je mets une balise span dans ma balise avec le bon identifiant.
<h3><span id="one"></span>one</h3>
Maintenant, utilisez 2 lignes de CSS pour les positionner correctement.
h3{ position:relative; }
h3 span{ position:absolute; top:-200px;}
Modifiez la valeur supérieure pour qu'elle corresponde à la hauteur de votre en-tête fixe (ou plus). Maintenant, je suppose que cela fonctionnerait également avec d’autres éléments.
FWIW cela a fonctionné pour moi:
*[id]:before {
display: block;
content: " ";
margin-top: -75px;
height: 75px;
visibility: hidden;
}
Solution Pure CSS inspirée par Alexander Savin:
a[name] {
padding-top: 40px;
margin-top: -40px;
display: inline-block; /* required for webkit browsers */
}
Si vous le souhaitez, vous pouvez éventuellement ajouter les éléments suivants si la cible est toujours hors de l'écran:
vertical-align: top;
Cela prend beaucoup d'éléments des réponses précédentes et combine dans une minuscule fonction anonyme jQuery (194 octets minifiés). Ajustez fixedElementHeight à la hauteur de votre élément de menu ou de blocage.
(function($, window) {
var adjustAnchor = function() {
var $anchor = $(':target'),
fixedElementHeight = 100;
if ($anchor.length > 0) {
$('html, body')
.stop()
.animate({
scrollTop: $anchor.offset().top - fixedElementHeight
}, 200);
}
};
$(window).on('hashchange load', function() {
adjustAnchor();
});
})(jQuery, window);
Si vous n'aimez pas l'animation, remplacez
$('html, body')
.stop()
.animate({
scrollTop: $anchor.offset().top - fixedElementHeight
}, 200);
avec:
window.scrollTo(0, $anchor.offset().top - fixedElementHeight);
Version altérée:
!function(o,n){var t=function(){var n=o(":target"),t=100;n.length>0&&o("html, body").stop().animate({scrollTop:n.offset().top-t},200)};o(n).on("hashchange load",function(){t()})}(jQuery,window);
Ma solution combine les sélecteurs cible et avant pour notre CMS. Les autres techniques ne prennent pas en compte le texte dans l'ancre. Ajustez la hauteur et la marge négative au décalage dont vous avez besoin ...
:target:before {
content: "";
display: block;
height: 180px;
margin: -180px 0 0;
}
J'avais été confronté à un problème similaire. Malheureusement, après avoir mis en œuvre toutes les solutions ci-dessus, je suis parvenu à la conclusion suivante.
J'ai écrit ce simple texte défilant js, qui tient compte du décalage dû à l'en-tête et a déplacé la div d'environ 125 pixels en dessous. S'il vous plaît, utilisez-le comme bon vous semble.
Le HTML
<div id="#anchor"></div> <!-- #anchor here is the anchor tag which is on your URL -->
Le javascript
$(function() {
$('a[href*=#]:not([href=#])').click(function() {
if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'')
&& location.hostname == this.hostname) {
var target = $(this.hash);
target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
if (target.length) {
$('html,body').animate({
scrollTop: target.offset().top - 125 //offsets for fixed header
}, 1000);
return false;
}
}
});
//Executed on page load with URL containing an anchor tag.
if($(location.href.split("#")[1])) {
var target = $('#'+location.href.split("#")[1]);
if (target.length) {
$('html,body').animate({
scrollTop: target.offset().top - 125 //offset height of header here too.
}, 1000);
return false;
}
}
});
Voir un implémentation en direct ici .
Pour les navigateurs modernes, ajoutez simplement le sélecteur CSS3: target à la page. Cela s'appliquera à toutes les ancres automatiquement.
:target {
display: block;
position: relative;
top: -100px;
visibility: hidden;
}
a[id]:before {
content:"";
display:block;
height:50px;
margin:-30px 0 0;
}
Cela va ajouter un pseudo-élément avant chaque balise a avec un identifiant. Ajustez les valeurs pour correspondre à la hauteur de votre en-tête.
Comme @moeffju le suggère, cela peut être réalisé avec CSS . La question que j’ai rencontrée (et qui me surprend de ne pas avoir discuté de la question) est la superposition d’éléments précédents superposés avec un remplissage ou une bordure transparente empêche le survol et les actions de clic au bas de ces sections car le suivant ordre z.
La meilleure solution que j'ai trouvée consistait à placer le contenu de la section dans un div
qui se trouve à z-index: 1
:
// Apply to elements that serve as anchors
.offset-anchor {
border-top: 75px solid transparent;
margin: -75px 0 0;
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
}
// Because offset-anchor causes sections to overlap the bottom of previous ones,
// we need to put content higher so links aren't blocked by the transparent border.
.container {
position: relative;
z-index: 1;
}
Les solutions avec des propriétés de changement de position ne sont pas toujours possibles (cela peut détruire la mise en page) donc je suggère ceci:
HTML:
<a id="top">Anchor</a>
CSS:
#top {
margin-top: -250px;
padding-top: 250px;
}
Utilisez ceci:
<a id="top"> </a>
minimiser les chevauchements et définir la taille de la police sur 1px. Une ancre vide ne fonctionnera pas dans certains navigateurs.
Pour le même problème, j'ai utilisé une solution simple: placez un capuchon de 40 pixels sur chaque ancre.
En empruntant une partie du code d’une réponse donnée sur ce lien (aucun auteur n’est spécifié), vous pouvez inclure un effet de défilement lisse de Nice à l’ancre, tout en l’arrêtant à -60px au-dessus de l’ancre, s'ajustant bien sous la barre de navigation fixe bootstrap (nécessite jQuery):
$(".dropdown-menu a[href^='#']").on('click', function(e) {
// prevent default anchor click behavior
e.preventDefault();
// animate
$('html, body').animate({
scrollTop: $(this.hash).offset().top - 60
}, 300, function(){
});
});
J'ai rencontré le même problème et j'ai fini par gérer les événements de clic manuellement, comme suit:
$('#mynav a').click(() ->
$('html, body').animate({
scrollTop: $($(this).attr('href')).offset().top - 40
}, 200
return false
)
Faites défiler l'animation facultative, bien sûr.
Les méthodes ci-dessus ne fonctionnent pas très bien si votre ancre est un élément de tableau ou au sein d'un tableau (ligne ou cellule).
Je devais utiliser javascript et me lier à l'événement window hashchange
pour contourner ce problème ( démo ):
function moveUnderNav() {
var $el, h = window.location.hash;
if (h) {
$el = $(h);
if ($el.length && $el.closest('table').length) {
$('body').scrollTop( $el.closest('table, tr').position().top - 26 );
}
}
}
$(window)
.load(function () {
moveUnderNav();
})
.on('hashchange', function () {
moveUnderNav();
});
* Remarque: L'événement hashchange n'est pas disponible dans tous les navigateurs.
Au lieu d’avoir une barre de navigation à position fixe qui est sous-jacente au reste du contenu de la page (le corps de la page entier est déroulable), envisagez plutôt d’avoir un corps non défilable avec une barre de navigation statique, puis le contenu de la page dans div défilement absolument positionné ci-dessous.
C'est-à-dire, avoir du HTML comme ça ...
<div class="static-navbar">NAVBAR</div>
<div class="scrollable-content">
<p>Bla bla bla</p>
<p>Yadda yadda yadda</p>
<p>Mary had a little lamb</p>
<h2 id="stuff-i-want-to-link-to">Stuff</h2>
<p>More nonsense</p>
</div>
... et CSS aiment ça:
.static-navbar {
height: 100px;
}
.scrollable-content {
position: absolute;
top: 100px;
bottom: 0;
overflow-y: scroll;
width: 100%;
}
Cela permet d’obtenir le résultat souhaité de manière simple et sans hacky. La seule différence de comportement entre ceci et certains des astucieux CSS suggérés ci-dessus est que la barre de défilement (dans les navigateurs qui en rendent un) sera attachée à la div content plutôt qu'à la hauteur totale de la page. Vous pouvez ou ne pas considérer cela souhaitable.
Cela a été inspiré par la réponse de Shouvik - même concept que le sien, seule la taille de l'en-tête fixe n'est pas codée en dur. Tant que votre en-tête fixe est dans le premier noeud d'en-tête, cela devrait "fonctionner"
/*jslint browser: true, plusplus: true, regexp: true */
function anchorScroll(fragment) {
"use strict";
var amount, ttarget;
amount = $('header').height();
ttarget = $('#' + fragment);
$('html,body').animate({ scrollTop: ttarget.offset().top - amount }, 250);
return false;
}
function outsideToHash() {
"use strict";
var fragment;
if (window.location.hash) {
fragment = window.location.hash.substring(1);
anchorScroll(fragment);
}
}
function insideToHash(nnode) {
"use strict";
var fragment;
fragment = $(nnode).attr('href').substring(1);
anchorScroll(fragment);
}
$(document).ready(function () {
"use strict";
$("a[href^='#']").bind('click', function () {insideToHash(this); });
outsideToHash();
});
Vous pouvez y parvenir sans identifiant à l'aide du sélecteur a[name]:not([href])
css. Cela cherche simplement des liens avec un nom et sans href, par exemple. <a name="anc1"></a>
Un exemple de règle pourrait être:
a[name]:not([href]){
display: block;
position: relative;
top: -100px;
visibility: hidden;
}
Je suis confronté à ce problème sur un site Web TYPO3, où tous les "éléments de contenu" sont entourés de quelque chose comme:
<div id="c1234" class="contentElement">...</div>
et j'ai changé le rendu de sorte qu'il se présente comme ceci:
<div id="c1234" class="anchor"></div>
<div class="contentElement">...</div>
Et ce CSS:
.anchor{
position: relative;
top: -50px;
}
La barre supérieure fixe ayant une hauteur de 40px, les ancres fonctionnent à nouveau et démarrent 10px sous la barre supérieure.
Le seul inconvénient de cette technique est que vous ne pouvez plus utiliser :target
.
Une autre astuce de l'excellent réponse de @Jan est de l'incorporer dans l'en-tête fixe #uberbar, qui utilise jQuery (ou MooTools). ( http://davidwalsh.name/persistent-header-opacity )
J'ai modifié le code pour que le haut du contenu se trouve toujours en dessous, pas sous l'en-tête fixe, mais j'ai également ajouté à nouveau les ancres de @Jan en s'assurant que les ancres sont toujours positionnées sous l'en-tête fixe.
Le CSS:
#uberbar {
border-bottom:1px solid #0000cc;
position:fixed;
top:0;
left:0;
z-index:2000;
width:100%;
}
a.anchor {
display: block;
position: relative;
visibility: hidden;
}
Le jQuery (y compris des ajustements à la fois sur les approches #uberbar et anchor:
<script type="text/javascript">
$(document).ready(function() {
(function() {
//settings
var fadeSpeed = 200, fadeTo = 0.85, topDistance = 30;
var topbarME = function() { $('#uberbar').fadeTo(fadeSpeed,1); }, topbarML = function() { $('#uberbar').fadeTo(fadeSpeed,fadeTo); };
var inside = false;
//do
$(window).scroll(function() {
position = $(window).scrollTop();
if(position > topDistance && !inside) {
//add events
topbarML();
$('#uberbar').bind('mouseenter',topbarME);
$('#uberbar').bind('mouseleave',topbarML);
inside = true;
}
else if (position < topDistance){
topbarME();
$('#uberbar').unbind('mouseenter',topbarME);
$('#uberbar').unbind('mouseleave',topbarML);
inside = false;
}
});
$('#content').css({'margin-top': $('#uberbar').outerHeight(true)});
$('a.anchor').css({'top': - $('#uberbar').outerHeight(true)});
})();
});
</script>
Et enfin le HTML:
<div id="uberbar">
<!--CONTENT OF FIXED HEADER-->
</div>
....
<div id="content">
<!--MAIN CONTENT-->
....
<a class="anchor" id="anchor1"></a>
....
<a class="anchor" id="anchor2"></a>
....
</div>
C’est peut-être utile pour ceux qui aiment l’en-tête à atténuation progressive #uberbar!
En ajoutant à la réponse de Ziav (et merci à Alexander Savin), je dois utiliser l’ancienne école <a name="...">...</a>
comme nous utilisons <div id="...">...</div>
à une autre fin dans notre code. J'ai eu quelques problèmes d'affichage avec display: inline-block
- la première ligne de chaque élément <p>
s'avérait légèrement en retrait (sur les navigateurs Webkit et Firefox). J'ai fini par essayer d'autres valeurs display
et display: table-caption
fonctionne parfaitement pour moi.
.anchor {
padding-top: 60px;
margin-top: -60px;
display: table-caption;
}
J'ai ajouté un élément .vspace
de 40px-height tenant l'ancre avant chacun de mes éléments h1
.
<div class="vspace" id="gherkin"></div>
<div class="page-header">
<h1>Gherkin</h1>
</div>
En CSS:
.vspace { height: 40px;}
Cela fonctionne très bien et l'espace n'est pas choquant.
que diriez-vous des balises span cachées avec des identifiants pouvant être liés qui fournissent la hauteur de la barre de navigation:
#head1 {
padding-top: 60px;
height: 0px;
visibility: hidden;
}
<span class="head1">somecontent</span>
<h5 id="headline1">This Headline is not obscured</h5>
voici le violon: http://jsfiddle.net/N6f2f/7
Vous pouvez également ajouter une ancre avec suivez attr:
(text-indent:-99999px;)
visibility: hidden;
position:absolute;
top:-80px;
et donnez au conteneur parent une position relative.
Fonctionne parfaitement pour moi.