la compensation d'une ancre html pour ajuster l'en-tête fixe [dupliquer]
cette question a déjà une réponse ici:
- en-tête de page fixe chevauche les ancres de page 26 réponses
j'essaie de nettoyer la façon dont mes ancres fonctionnent. J'ai un en-tête qui est fixé à la partie supérieure de la page, de sorte que, lorsque vous vous connectez à un point d'ancrage ailleurs dans la page, la page saute si l'ancre est en haut de la page, en laissant le contenu derrière l'en-tête fixe (j'espère qu'un sens). J'ai besoin d'un moyen de compenser l'ancre par l'25px à partir de la hauteur de l'en-tête. Je préférerais HTML ou CSS, mais Javascript serait également acceptable.
28 réponses
vous pouvez simplement utiliser CSS sans javascript.
donnez à votre ancre une classe:
<a class="anchor" id="top"></a>
vous pouvez alors positionner l'ancre 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 relativement. - 250px va positionner l'ancre vers le haut 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 pas de vide dans le contenu et les liens d'ancrage fonctionne vraiment bien.
comme il s'agit d'une préoccupation 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 hacky, créent des problèmes de flux potentiels, et ne peuvent pas répondre dynamiquement aux changements dans le DOM / viewport.
avec cela à l'esprit, je crois que L'utilisation de JavaScript est encore (février 2017) la meilleure approche. Dessous est une solution vanille-JS qui répondra à la fois aux clics d'ancrage et résoudre le hachage de page sur charge (voir JSFiddle) . Modifier la méthode .getFixedOffset()
si des calculs dynamiques sont nécessaires. Si vous utilisez jQuery, voici une solution modifiée avec une meilleure délégation d'événement et un défilement en douceur .
(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 aussi une solution. Dans mon cas, c'était assez facile.
j'ai une liste de menu 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 au-dessous des rubriques où elle doit être placée.
<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 le faire passer à mon étiquette parce que ce serait derrière le menu.
à la place j'ai mis une étiquette de portée à l'intérieur de mon étiquette avec le une pièce d'identité valable.
<h3><span id="one"></span>one</h3>
utilisez maintenant 2 lignes de css pour les positionner correctement.
h3{ position:relative; }
h3 span{ position:absolute; top:-200px;}
changez la valeur supérieure pour correspondre à la hauteur de votre en-tête fixe (ou plus). Maintenant, je suppose que cela fonctionnerait avec d'autres éléments aussi bien.
Pur css solution inspirée par Alexandre Savin:
a[name] {
padding-top: 40px;
margin-top: -40px;
display: inline-block; /* required for webkit browsers */
}
vous pouvez éventuellement ajouter ce qui suit si la cible est toujours hors de l'écran:
vertical-align: top;
FWIW cela a fonctionné pour moi:
*[id]:before {
display: block;
content: " ";
margin-top: -75px;
height: 75px;
visibility: hidden;
}
j'avais été confronté à un problème similaire, malheureusement après avoir mis en œuvre toutes les solutions ci-dessus, je suis venu à la conclusion suivante.
- mes éléments intérieurs avaient une structure CSS fragile et la mise en œuvre d'une position relative / jeu absolu, était complètement briser la conception de la page.
- CSS n'est pas mon fort.
j'ai écrit ce simple scrolling js, qui rend compte de l'offset causé par le en-tête et relocalisé le div environ 125 pixels ci-dessous. Veuillez l'utiliser comme bon vous semble.
the 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 en direct de la mise en œuvre d'ici .
cela prend beaucoup d'éléments des réponses précédentes et se combine dans une minuscule (194 bytes miniified) fonction anonyme jQuery. Réglez fixedElementHeight pour la hauteur de votre menu ou élément 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);
Uglified version:
!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);
vous pouvez le faire sans js et sans modifier html. Ses feuilles de style css.
a[id]:before {
content:"";
display:block;
height:50px;
margin:-30px 0 0;
}
qui ajoutera un pseudo-élément avant chaque étiquette a avec un id. Ajuster les valeurs à la hauteur de votre en-tête.
pour les navigateurs modernes, il suffit d'ajouter le CSS3 :target selector à la page. Cela s'appliquera automatiquement à tous les ancrages.
:target {
display: block;
position: relative;
top: -100px;
visibility: hidden;
}
ma solution combine la cible et les sélecteurs avant pour notre CMS. D'autres techniques ne tiennent pas compte du texte dans l'ancre. Réglez la hauteur et la marge négative au déport dont vous avez besoin...
:target:before {
content: "";
display: inline-block;
height: 180px;
margin: -180px 0 0;
}
comme le suggère @moeffju, cela peut être réalisé avec CSS . Le problème que j'ai rencontré (dont je suis surpris que je n'ai pas vu discuté) est le truc du chevauchement des éléments précédents avec du rembourrage ou une bordure transparente empêche les actions de vol stationnaire et de clic au bas de ces sections parce que le suivant vient plus haut dans l'ordre de z.
le meilleur correctif que j'ai trouvé était de placer le contenu de la section dans un div
qui est à 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;
}
pour le même problème, j'ai utilisé une solution facile : mettre un dessus de rembourrage de 40px sur chaque ancre.
Solutions avec changement de propriété de position ne sont pas toujours possibles (il peut détruire la disposition) 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>
pour minimiser le chevauchement, et mettre la taille de police à 1px. L'ancre vide ne fonctionnera pas dans certains navigateurs.
empruntant une partie du code d'une réponse donnée à ce lien (aucun auteur n'est spécifié), vous pouvez inclure un bel effet de scroll à l'ancre, tout en le faisant s'arrêter à-60px au-dessus de l'ancre, bien ajusté sous la barre de navigation bootstrap fixe (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é ce même problème et j'ai fini par Gérer les clics manuellement, comme:
$('#mynav a').click(() ->
$('html, body').animate({
scrollTop: $($(this).attr('href')).offset().top - 40
}, 200
return false
)
animation par défilement optionnel, bien sûr.
les méthodes ci-dessus ne fonctionnent pas très bien si votre ancre est un élément de table ou à l'intérieur d'une table (rangée ou cellule).
j'ai dû utiliser javascript et lier à la fenêtre hashchange
événement pour travailler autour de cela ( 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();
});
* Note: l'événement hashchange n'est pas disponible dans tous les navigateurs.
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é dur. Aussi longtemps que votre en-tête fixe est dans le premier noeud d'en-tête, cela devrait "juste 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();
});
au lieu d'avoir une barre de navigation à position fixe qui est sous-tendue par le reste du contenu de la page (avec le corps entier de la page étant scrollable), envisager à la place d'avoir un corps non scrollable avec une barre de navigation statique et avoir ensuite le contenu de la page dans un div absolument scrollable positionné ci-dessous.
C'est-à-dire, avoir HTML comme ceci...
<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 comme ceci:
.static-navbar {
height: 100px;
}
.scrollable-content {
position: absolute;
top: 100px;
bottom: 0;
overflow-y: scroll;
width: 100%;
}
cela permet d'atteindre le résultat souhaité d'une manière simple et sans détour. La seule différence de comportement entre ceci et quelques-uns des hacks astucieux CSS suggérés ci-dessus est que le scrollbar (dans les navigateurs qui en rendent un) sera attaché à la div de contenu plutôt que toute la hauteur de la page. Vous pouvez juger cela souhaitable ou non.
je suis confronté à ce problème dans un site Web de type 3, où tous les "éléments de contenu" sont enveloppés avec quelque chose comme:
<div id="c1234" class="contentElement">...</div>
et j'ai changé le rendu pour qu'il rende 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 étant haute de 40px, maintenant les ancres fonctionnent à nouveau et démarrez 10px sous la barre supérieure.
Seul inconvénient de cette technique est que vous ne pouvez plus utiliser :target
.
ajoutant à la réponse de Ziav (grâce à Alexander Savin), j'ai besoin d'utiliser la vieille école <a name="...">...</a>
comme nous utilisons <div id="...">...</div>
pour un autre but dans notre code. J'ai eu quelques problèmes d'affichage en utilisant display: inline-block
-- la première ligne de chaque élément <p>
s'est avérée être légèrement indentée à droite (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é l'élément .vspace
de hauteur 40px tenant l'ancre devant chacun de mes éléments h1
.
<div class="vspace" id="gherkin"></div>
<div class="page-header">
<h1>Gherkin</h1>
</div>
dans la CSS:
.vspace { height: 40px;}
ça marche très bien et l'espace n'est pas en mouvement.
qu'en est-il des étiquettes à portée cachée avec des identificateurs linkables 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>
heres le violon: http://jsfiddle.net/N6f2f/7
vous pouvez aussi ajouter une ancre avec l'attr suivant:
(text-indent:-99999px;)
visibility: hidden;
position:absolute;
top:-80px;
et donner au conteneur parent une position relative.
est parfait pour moi.
vous pouvez y arriver sans ID en utilisant le sélecteur CSS a[name]:not([href])
. Cela recherche simplement des liens avec un nom et non href par exemple <a name="anc1"></a>
une règle type pourrait être:
a[name]:not([href]){
display: block;
position: relative;
top: -100px;
visibility: hidden;
}
une autre touche à l'excellente 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 soit toujours en dessous et non sous l'en-tête fixe et j'ai aussi ajouté les ancres de @Jan en s'assurant que les ancres sont toujours positionnées sous l'en-tête fixe.
La 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;
}
jQuery (y compris les ajustements à la fois le #uberbar et l'ancrage des approches:
<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>
peut-être que c'est utile pour quelqu'un qui aime l'en-tête #uberbar fading dixed!
la solution de @AlexanderSavin fonctionne très bien dans les navigateurs WebKit
pour moi.
j'ai en outre dû utiliser : cible pseudo-classe qui applique le style à l'ancre sélectionnée pour ajuster le rembourrage dans FF
, Opera
& IE9
:
a:target {
padding-top: 40px
}
notez que ce style n'est pas pour Chrome
/ Safari
donc vous devrez probablement utiliser CSS-hacks, commentaires conditionnels, etc.
aussi, je voudrais noter que la solution D'Alexander fonctionne en raison du fait que l'élément ciblé est inline
. Si vous ne voulez pas de lien, vous pouvez simplement changer display
propriété:
<div id="myanchor" style="display: inline">
<h1 style="padding-top: 40px; margin-top: -40px;">My anchor</h1>
</div>
Voici la solution que nous utilisons sur notre site. Réglez la variable headerHeight
quelle que soit la hauteur de votre en-tête. Ajoutez la classe js-scroll
à l'ancre qui devrait défiler sur le clic.
// SCROLL ON CLICK
// --------------------------------------------------------------------------
$('.js-scroll').click(function(){
var headerHeight = 60;
$('html, body').animate({
scrollTop: $( $.attr(this, 'href') ).offset().top - headerHeight
}, 500);
return false;
});