Fixed page header overlaps in-page anchors
Solution 1
I had the same problem. I solved it by adding a class to the anchor element with the topbar height as the padding-top value.
<h1><a class="anchor" name="barlink">Bar</a></h1>
I used this CSS:
.anchor { padding-top: 90px; }
Solution 2
If you can’t or don’t want to set a new class, add a fixed-height ::before
pseudo-element to the :target
pseudo-class in CSS:
:target::before {
content: "";
display: block;
height: 60px; /* fixed header height*/
margin: -60px 0 0; /* negative fixed header height */
}
Or scroll the page relative to :target
with jQuery:
var offset = $(':target').offset();
var scrollto = offset.top - 60; // minus fixed header height
$('html, body').animate({scrollTop:scrollto}, 0);
Solution 3
html {
scroll-padding-top: 70px; /* height of sticky header */
}
from: https://css-tricks.com/fixed-headers-on-page-links-and-overlapping-content-oh-my/
Solution 4
I use this approach:
/* add class="jumptarget" to all targets. */
.jumptarget::before {
content:"";
display:block;
height:50px; /* fixed header height*/
margin:-50px 0 0; /* negative fixed header height */
}
It adds an invisible element before each target. It works IE8+.
Here are more solutions: http://nicolasgallagher.com/jump-links-and-viewport-positioning/
Solution 5
From 4/2021 there is single non-hacky and fully cross-browser solution:
h1 {
scroll-margin-top: 50px
}
It is part of CSS Scroll Snap spec. Runs on all modern browsers. (For more context, see the MDN pages about scroll-margin-top
, scroll-padding-top
)
Tomalak
I know a bit about SQL, Regular Expressions, XSLT, ColdFusion, JavaScript, Python, PowerShell and scripting languages in general. I also have a Unicorn. :) Further I have a Twitter account I update once in a while, but you won't read much technical stuff there.
Updated on July 29, 2022Comments
-
Tomalak almost 2 years
If I have a non-scrolling header in an HTML page, fixed to the top, having a defined height:
Is there a way to use the URL anchor (the
#fragment
part) to have the browser scroll to a certain point in the page, but still respect the height of the fixed element without the help of JavaScript?http://example.com/#bar
WRONG (but the common behavior): CORRECT: +---------------------------------+ +---------------------------------+ | BAR///////////////////// header | | //////////////////////// header | +---------------------------------+ +---------------------------------+ | Here is the rest of the Text | | BAR | | ... | | | | ... | | Here is the rest of the Text | | ... | | ... | +---------------------------------+ +---------------------------------+
-
vlad saling over 12 yearsthis solution works indeed, but it is not bulletproof and it requires lot of finetuning for different browsers :( good try thoug. I am searching for a solution without extra markup.
-
theorise over 12 yearsShame this is so messy, means you can't just use IDs. Would love to find a solution, I have tried all sorts of things.
-
Tomalak about 12 yearsMh. Not bad, but still quite a hack (and does not work in IE, sadly).
-
mutttenxd over 11 years@Tomalak Be aware that this solution makes links inside the padding area non-clickable. To fix this you must use z-index, and set the links value higher than the anchor's. Remember you topbar should have the highest z-index value, so the page's content don't float ontop of the topbar when you scroll.
-
Knickedi over 10 yearsDid not work in Chrome v28 due to a WebKit fixed position bug causing header to disappear. This answer did the trick im my case.
-
zipzit about 10 yearsMuttenXd.. you are my hero. I had weird non-function link problem on my site (unrelated to anchors) been driving me crazy. Your non-clickable comment really helped. I owe ya big!
-
Jake Wilson about 9 yearsIsn't adding padding to the top of the anchor going to push it farther down? Don't you want it farther up instead in order to account for the fixed header?
-
fechnert almost 9 yearsuse
<span>
instead of<a>
to avoid the colored headlines. -
Skippy le Grand Gourou over 8 yearsAs suggested in Roy Shoa's answer, add
margin-top: -90px;
to counter the gap created by padding. -
jimmyplaysdrums over 8 yearsI used
.anchor:target {padding-top: 90px;}
so that it only added the padding when they used the anchor tag. This way there isn't always a bunch of padding if the page loads at the top. Only when it loads a that specific point lower on the page. -
Jamie Carl over 8 yearsThis is the solution that worked perfectly for me without stuffing up anything else in my page layout.
-
Boguslaw over 8 yearsIf you use anchor.js library, the css solution works perfectly.
-
Tomalak about 8 yearsThat's not hacky at all! Nice solution.
-
AJJ almost 8 yearsI have come to the same conclusion and solution. It seems like every body else is forgetting about external links that use the hashtags.
-
Admin over 7 years<pre><code> // handle hashes when page loads // <stackoverflow.com/a/29853395> function adjustAnchor() { const $anchor = $(':target'); const fixedElementHeight = $('.navbar-fixed-top').outerHeight(); if ($anchor.length > 0) window.scrollTo(0, $anchor.offset().top - fixedElementHeight); } $(window).on('hashchange load', adjustAnchor); $('body').on('click', "a[href^='#']", function (ev) { if (window.location.hash === $(this).attr('href')) { ev.preventDefault(); adjustAnchor(); } });</pre></code>
-
Mert S. Kaplan over 7 yearsNot ".target", should be ":target"
-
Mert S. Kaplan over 7 yearsThis solution has a problem when used with list in WebKit. Look: stackoverflow.com/questions/39547773/…
-
Mert S. Kaplan over 7 yearsCSS solution has a problem when used with list in WebKit. Look: stackoverflow.com/questions/39547773/…
-
webvitaly over 7 years@MertS.Kaplan ".target" is the class 'target'.
-
scavenger over 7 yearslook at the link Badabam provided, there is a second solution, which I use and which works in Chrome and Firefox
-
Jeffrey Simon about 7 yearsI was able to use a slightly modified version of this code from Thunder. I tried many of the CSS-only and simpler solutions on this page and on stackoverflow.com/questions/10732690/… and on stackoverflow.com/questions/10732690/… but they were not usable due to my requirements. The page is an FAQ page with many anchors to position to, both from on and off the page, so could have have lots of blank spots. Continued in next comment ....
-
Jeffrey Simon about 7 yearsTo make it work for me, here are the minor changes: (1) change "<=" in the href.index line to "<" to allow on-page navigation; (2) change "a.navigation" that had to be "#faq a" for my use; (3) change var fromTop = 250 to a ternary based on window.innerWidth for differences in mobile vs. desktop; (4) change 400 in the animate to 1, as the animation is not desired and 0 does not work; (5) add if ($('my-page-selector').length before the call to offSetScrollFromLocation in the anonymous function to prevent unnecessary calls on pages where not needed.
-
kwiat1990 about 7 yearsI have that problem today and I've used your solution. Although it seems that you don't need timeout. This works as IIFE as well.
-
Jpsy about 7 years@kwiat1990: Yes, that may be possible – but only if your JS is placed at the very end of the HTML code. Otherwise your scroll target might not yet be in the DOM. Actually that is the whole point of using onDomReady. So I guess the timeout version is the stable way to go.
-
krulik about 7 yearsIt won't work if the target has top padding or border since it relies on margin-collapse.
-
Suresh Karia almost 7 yearsBrilliant, Worked like a charm! thank you @Adripants , solved very old bug in using psuedo css without changing single dom element.
-
Endless almost 7 years
:target
was just Brilliant! -
Tomalak almost 7 yearsSolves the problem, but not the "without the help of Javascript" part...
-
Jarod Thornton almost 7 yearsI used a little javascript to add and remove padding as needed.
$(function(){ $('#jump').click(function() { $('#id').addClass("anchor"); }); $('.top').click(function() { $('#id').removeClass("anchor"); }); });
-
Giulio Caccin over 6 yearsOP asked not to use javascript.
-
Giulio Caccin over 6 yearsOP asked not to use javascript.
-
RachieVee over 6 yearsTested this on the latest Firefox (55.0.3 on PC) and it works now. :-)
-
Augusto Samamé Barrientos over 6 yearsThis answer was the only one that worked for external links. However it does not work for me when the link is called in the same page :( Any ideas?
-
snap over 6 yearsWorks with external links but not with in-page links.
-
Jesse over 6 years@Augusto did you find a solution? I am having the same problem.
-
Augusto Samamé Barrientos over 6 yearsUnfortunately, no :(
-
Avatar almost 6 yearsOnly FYI: If the targeted anchor element has style
display: table;
then it will not work. -
Tomalak almost 6 yearsThank you very much for sharing! Old as the thread is, there still does not seem to be one canonical solution to this problem.
-
twknab almost 6 years+SteveCinq You read my mind -- I was facing the same issues where shared solutions were not working as predicted. Adding in the
<span>
with the anchor in the span, along with adding in the padding and margin worked for me. Thanks so much for taking the time to submit this answer despite the age of the thread! -
Alireza over 5 yearsI loved :target
-
Tomalak over 5 yearsIsn't that exactly the same method that Guillaume Le Mière and a few others have shown in this thread?
-
Matt Underwood over 5 yearsIt is very similar but I think it is easier to understand and a more detailed response with an even more in-depth link to an outside source.
-
Tomalak over 5 yearsPragmatic, but there is a downside - it only works for the first heading on a page. If there are more headings, you will start to see large gaps in the text because of the
<br>
tags before each heading. -
Ali Kanat about 5 yearsPlease try to explain why you think this script would solve the problem
-
Brett almost 5 yearsFinally - I was using external anchors and none of the other solutions were working and I couldn't figure out why when everyone else was saying they were working. I did notice with this that my experience with IE was that it required twice the amount of delay to work. Perhaps a more solid method would be to put a small delay and then run a loop that checks if the element exists and when it does execute the code; perhaps with minor delays between each check.
-
Vanessa King almost 5 yearsThis is perfect—it worked without having to go back and add a class to the target ids. So simple and clean, thanks!
-
WoodrowShigeru over 4 yearsNote that this won't work if the target element itself happens to have
display: flex;
orpadding-top
. Do use a dedicated anchor element (like<a name="my_link">{leave this empty}</a>
) in those cases. -
Chaya Cooper over 4 yearsNote: This only worked with my code once I added
visibility: hidden;
to the:target::before
pseudo-element. -
CraZ over 4 yearsThe
:target::before
didn't work for us so I usedh1[id]::before, h2[id]::before, ..., h6[id]::before
instead (I needed to use it at headings only). -
Flimm over 4 yearsBrowser support: caniuse.com/#feat=mdn-css_properties_scroll-padding-top
-
Flimm over 4 yearsWhat is the difference between
scroll-padding-top
andscroll-margin-top
? -
davidloper over 4 yearsWorks for me. I am navigating to the anchor from another page. Others didn't work. Thanks!
-
Tomalak over 4 yearsA few others have discovered this solution before you, see the the answers on page 1, for example stackoverflow.com/a/56467997/18771
-
jamawe over 4 yearsSorry, haven't noticed! Thanks!
-
Juliusz Gonera over 4 yearsThis doesn't work for scrolling entire page in Edge and Safari (bugs.webkit.org/show_bug.cgi?id=179379).
-
Juliusz Gonera over 4 yearsThis doesn't work for scrolling entire page in Edge and Safari (bugs.webkit.org/show_bug.cgi?id=179379).
-
Jordy about 4 yearsThis is not working when the target is within a flexbox for me.
-
Priya Payyavula about 4 yearsSavior! Simple,on point. None of the other solutions worked for me because I am using display:flex. Thanks a ton, it was so frustrating trying all the solutions.
-
ich5003 about 4 yearsThank you very much, your CSS version was the only feasible solution for me, as I didn't want to use javascript and many other css solutions required changing the anchors which I wasn't able to, as they are generated by my CMS.
-
MacroMan almost 4 yearsIt is the same solution, but I can understand and follow this one easily. The others didn't give me enough information.
-
yehanny almost 4 yearsThis is a BEAUTIFUL solution, I used scroll-behavior: smooth !important; also to go anchor, really simple in two lines of code
-
Tomalak almost 4 years
scroll-margin-top
has been suggested earlier in this thread -
Mu-Tsun Tsai almost 4 yearsIn Safari it is called
scroll-snap-margin-top
: caniuse.com/#search=scroll-margin-top -
Crystal Gardner almost 4 yearsScroll margin is not applied for scrolls to fragment target...this will not work for anchor tags developer.mozilla.org/en-US/docs/Web/CSS/scroll-margin-top
-
thisismydesign almost 4 yearsFor me this created a 60px-high <a> tag that was masking other links that stopped working as a result. Try this instead: stackoverflow.com/a/13184714/2771889
-
Odair Augusto Trujillo Orozco almost 4 yearsThis one works for me, but not in Firefox for Linux version 78.
-
thiras over 3 yearsProbably the best solution I've ever seen.
-
ujifgc over 3 yearsWorks perfectly. I have a large table and span#anchors on some cells. Have set
td span[id] { scroll-margin-top: 80px; }
and it works, scrolling exactly with this offset. Much faster rendering than pseudo-elements with margin and padding. -
WoodrowShigeru over 3 years@thisismydesign, The answer you're pointing to has the same issue that you're claiming it solves.
-
Perry over 3 yearsThis should be the accepted answer now. The market has caught up.
-
Pixelcode over 3 yearsThanks for this amazing solution! I've always struggled with that problem until now when you – my new hero – reached my horizon. Thank you so much!
-
jamheadart over 3 yearsI was going to add something similar after trying SO many different techniques, I ended up having the same idea. After the start of any div that should be the anchor, I just include a span element with an anchor class and the id for it. I actually used these properties: content: " "; display: block; position: relative; padding-top: 100px; margin-top: -100px; visibility: hidden; pointer-events: none;
-
Stéphan Champagne over 3 yearsExcellent. Exactly what I was looking for. Credit is given to css-trick, NICE :)
-
dakur over 3 yearsIt is fixed in Safari TP now.
-
dakur over 3 yearsIt did not work in Safari for a long time, but it is fixed in TP now. See github.com/mdn/browser-compat-data/issues/…
-
user0474975 about 3 yearsOther solution I was using seemed to have stopped working in Chrome 89/Edge/IE. This solution fixed it.
-
user0474975 about 3 yearsI was using this solution for about a year and then it stopped working (Chrome 89, Edge) although it still works in Firefox.. The scroll-padding-top solution (below) does seem to work for Chrome, though.
-
mario about 3 yearsyou saved my day, it works very smoothly. (tested on firefox)
-
dakur almost 3 yearsReleased in 14.1 so it is fully cross-browser now!
-
dakur almost 3 yearsThis was released in Safari 14.1 as well on 4/2021.
-
dakur almost 3 yearsNote that in nowadays there is single no-hack fully cross-browser solution –
scroll-margin
/scroll-padding
. -
basZero almost 3 yearsThe
:target
solution does not work if the element is using display flex. -
codycustard almost 3 yearsThis should be the accepted answer, works in all but IE
-
Todd Gillette almost 3 yearsscroll-margin-top is also an option, though I haven't figured out under what conditions, if any, it would be preferable to scroll-padding-top.
-
Andreas almost 3 yearsYou might need to add also
z-index: -1
or something similar to:target
in case the slution is overlapping something it should not. -
Andreas almost 3 yearsok, after reading all the other answers and concidering, that we have now 2021, I made it work perfectly with
:target { scroll-margin-top: 60px; }
(kind of the best from both worlds) -
Andreas almost 3 yearsthat's my persona favorite:
:target { scroll-margin-top: 2em; }
-
Puddle over 2 yearsthis just creates a huge space of padding above the the targeted section which you easily notice when scrolling back up. very hacky solution
-
Mikko Rantalainen over 2 yearsI'd recommend using selector
:target
instead of given element names. -
Lideln Kyoku over 2 yearsUnfortunately this prevents clicking on elements that are above (before) the target element as they are overlapped by the pseudo-element. People should use
scroll-padding-top
unless they need IE11 -
Lideln Kyoku over 2 yearsWorks for me for all browsers except IE11. At least, now I can once again click on the links that are above/before this element.
-
Lideln Kyoku over 2 years@MikkoRantalainen what's the relation with the current answer? @Flimm it seems that using
scroll-padding-top
is preferrable in most cases.scroll-margin-top
allows for more flexibility in some scenarii only. -
Lideln Kyoku over 2 yearsIt prevents clicks on links/elements that are above/before the target element. It may have been the least of two evils earlier, but nowadays people should only use
scroll-padding-top
. -
Mikko Rantalainen over 2 yearsIf you have fixed or sticky page header, using
:target
selector fixes all fragment anchor usage instead of only selected elements (and leaving other targets incorrect). Using:target
instead of*
as the selector avoids applying this property to every element in the DOM. -
Tomalak over 2 yearsThat's pretty useful!
-
MarsAndBack over 2 yearsVery elegant; no messing with the layout. Works for me! I would still add JS to update the height value dynamically.
-
binglong li about 2 yearsThis is a better answer.stackoverflow.com/questions/10732690/…. For anchor on the middle of content,When there is no content below and window can not be scroll, The :target::before will suddenly add some empty block.
-
basZero about 2 yearsDoes not work in my case (using Bootstrap 4.5 row with an anchor)