43

I'm using a static bar at the top of my site, about 20px high. When I click an anchor link(for those who don't know, the navigation on wikipedia works like that. Click a title and the browser goes down to it) part of the text disappears behind that top bar.

Is there any way to stop this from happening? I'm not in a position where I can use an iFrame. Onlything I can think of is make it scroll back a bit each time, but is there another way? Some CSS setting to manipulate the body or something?

Koen027
  • 823
  • 3
  • 8
  • 15
  • I'd leave the iframe out of the question, that makes it more confusing. – PJ Brunet Jun 05 '18 at 01:27
  • "Is there another way?" Yes, use JavaScript to change the browser's default behavior. By default, the anchor isn't scrolled into view far enough. – PJ Brunet Jun 09 '18 at 16:37
  • 1
    Possible duplicate of [Fixed page header overlaps in-page anchors](https://stackoverflow.com/questions/4086107/fixed-page-header-overlaps-in-page-anchors) – PJ Brunet Jun 09 '18 at 17:25

7 Answers7

51

You could just use CSS without any javascript.

Give your anchor a class:

<a class="anchor"></a>

You can then position the anchor an offset higher or lower than where it actually appears on the page, by making it a block element and relatively positioning it. -250px will position the anchor up 250px

a.anchor{display: block; position: relative; top: -250px; visibility: hidden;}

By Jan see offsetting an html anchor to adjust for fixed header

Community
  • 1
  • 1
FredTheLover
  • 1,009
  • 10
  • 19
  • 1
    Aha, so the `display: block` was the problem. I tried doing a similar thing but the browser would ignore the relative offset, and I ended up with `a[name] {position: absolute; height: 150px; margin-top: -150px;}`. – riv May 13 '14 at 11:33
  • This works great. This problem has bothered me for years. Thanks! – MTAdmin Jan 28 '15 at 16:04
  • 5
    This has to be one of the most spectacular CSS tricks I've ever seen. – David T Feb 28 '17 at 13:33
  • I am glad I found this answer, and it helped me solve my problem. I still would like to know WHY it happens sometimes that the scroll goes down too far. Perhaps its just the placement of the anchor is too low in the markup? – Craig London Aug 31 '17 at 04:01
  • Should be the accepted answer. Well done. – jtubre Dec 13 '20 at 21:17
43

Here's a modern, one-line, pure CSS solution:

scroll-margin-top: 20px;

It does exactly what it sounds like: acts as if there is a margin, but only when in the context of scrolling. So scroll-margin-top: 20px; on an element means that clicking an anchor tag for that element will scroll the page to 20px above the element.

Compatibility (See caniuse)

No support for IE. Also, Safari versions 11-14 use a non-standard name: scroll-snap-margin-top (but otherwise works as expected). Safari 14.1+ works with the standard name.

Full example:

nav{
  position: fixed;
  top:0;
  left:0;
  background: black;
  color: white;
  width: 100%;
}

#after-nav{
  margin-top: 25px;
}

#section-1{
  width: 100%;
  background: green;
  height: 500px;
  scroll-margin-top: 20px;
}

#section-2{
  width: 100%;
  background: blue;
  height: 500px;
  scroll-margin-top: 20px;
}

ul{
margin-top: 0;
}
<nav>
Nav bar: 20px (as in OP)
</nav>
<div id="after-nav">
Table of contents:
<ul>
<li><a href="#section-1">Section 1</a></li>
<li><a href="#section-2">Section 2</a></li>
</ul>


<div id="section-1">Section 1</div>
<div id="section-2">Section 2</div>
</div>
Skeets
  • 4,476
  • 2
  • 41
  • 67
9

To fix this with CSS you can add a padding to the Elements you want to jump to:

Example

Alternatively, you could add a border:

div{ 
  height: 650px; 
  background:#ccc; 
  /*the magic happens here*/
  border-top:42px solid #fff;
}
ul{
  top: 0; 
  width: 100%; 
  height:20px; 
  position: fixed; 
  background: deeppink; 
  margin:0;
  padding:10px; 
}
li{
  float:left;
  list-style:none;
  padding-left:10px;
}
div:first-of-type{ 
  margin-top:0; 
}
<!-- content to be placed inside <body>…</body> -->
<ul>
  <li><a href="#s1">link 1</a>
  <li><a href="#s2">link 2</a>
  <li><a href="#s3">link 3</a>
  <li><a href="#s4">link 4</a>
</ul>
<div id="s1" class="first">1</div>
<div id="s2">2</div>
<div id="s3">3</div>
<div id="s4">4</div>

However, this is not always applicable.

For a javascript solution you could use a click event attached to the anchor elements that scrolls an adjusted amount of pixels like following:

document.querySelector("a").addEventListener("click",function(e){
    // dynamically determining the height of your navbar
    let navbar = document.querySelector("nav");
    let navbarheight = parseInt(window.getComputedStyle(navbar).height,10);
    // show 5 pixels of previous section just for illustration purposes 
    let scrollHeight = document.querySelector(e.target.hash).offsetTop - navbarheight - 5;
    /* scrolling to the element taking the height of the static bar into account*/
    window.scroll(0,scrollHeight);
    /*properly updating the window location*/
    window.location.hash = e.target.hash;
    /* do not execute default action*/
    e.preventDefault();
});
nav{
  position:fixed;
  top:0;
  left:0;
  right:0;
  height:40px;
  text-align:center;
  background:#bada55;
  margin:0;
}
a{
  display:block;
  padding-top:40px;
}
#section1{
  height:800px;
  background:repeating-linear-gradient(45deg,#606dbc55,#606dbc55 10px,#46529855 10px,#46529855 20px);
}
#section2{
  height:800px;
  background:repeating-linear-gradient(-45deg,#22222255,#22222255 10px,#66666655 10px,#66666655 20px);
}
<nav>static header</nav>
<a href="#section2">jump to section 2</a> 
<div id="section1">Section 1</div>
<div id="section2">Section 2</div>
Alex Ljamin
  • 737
  • 8
  • 31
Christoph
  • 50,121
  • 21
  • 99
  • 128
  • 2
    What event I should attach? Please provide full jQuery example. – Arugin Oct 14 '14 at 14:45
  • @Arugin I figured it out in jQuery https://stackoverflow.com/a/50690779/722796 no padding necessary, just don't scroll too far. Padding is the wrong solution because it might break the web design. – PJ Brunet Jun 05 '18 at 01:23
  • @PJBrunet First, if you read the question carefully, OP did not include the JS tag, that's why I gave a CSS solution first (that's what he is explicitely mentioning). Also, I see no reason why padding should "break" a well crafted web design. Second, I also provide a concise javascript solution, so what's the reason for the downvote? – Christoph Jun 06 '18 at 19:45
  • I'm sorry but I don't agree padding is an acceptable solution. For example, using a popular Envato theme, all my anchors display 90 pixels too high on the screen (titles completely hidden under a navbar) and if you try adding 90 pixels to every anchor it looks off. I think most designers would agree this would be a heavy handed approach. Z-indexed navbars are popular now. I've encountered this problem a few times with different designs and it would be too much padding. Frankly, I consider this a browser bug. The browser should be responsible for knowing if an anchor is hidden under a navbar. – PJ Brunet Jun 07 '18 at 08:21
  • I want to also point out, even with no navbar, it seems browsers are designed to bring the anchor right to the edge of the screen, which already looks bad. So navbars just compound the problem. For example, on this very page you're looking at, if you click the "share" link you can see the problem--the upvote arrow is chopped off the top of the page, then looks like it's nudged down with JS. Do you think StackOverflow should add even more padding at the top of each answer? If you ask me the padding looks beautiful and we shouldn't disrupt design proportions to fix a browser scrolling bug. – PJ Brunet Jun 07 '18 at 08:44
  • Well, the OP obviously disagrees with you in whether the padding fix is an "acceptable" solution - especially when they where looking for a CSS solution. – Christoph Jun 08 '18 at 15:14
  • @PJBrunet Why should this be a browser bug? It behaves exactly as intended in the specification in scrolling to the alignment point of the padding edge of the element. It's not the browser concern considering elements that are taken out of the document flow. – Christoph Jun 08 '18 at 17:33
  • HTML anchors were created for people to locate content on a page, like for a "table of contents." If the desired content can't be located in a presentable way because it's hidden under a navbar, I consider that a flaw in the browser. In terms of UX, the browser should do what the user wants, show the content. From a developer perspective, higher z-indexes shouldn't necessarily influence the scrolling behavior of lower z-indexes. So I wouldn't expect this to change in future browsers, but with AI a browser could 'detect' what a navbar is and adjust the anchor scrolling experience accordingly. – PJ Brunet Jun 09 '18 at 17:07
7

CSS-only: it's a little dirty, but :target {padding-top: 20px;} would work if you are linking to a block element (I assumed you do, since your question says div). However, it might not look so good when you scroll manually afterwards. Example http://dabblet.com/gist/3121729

Still, I think that using a bit of JavaScript to fix this would be nicer.

Ana
  • 35,599
  • 6
  • 80
  • 131
  • 1
    This didn't work for me (I am using bootstrap), but a variation described here did: https://www.itsupportguides.com/knowledge-base/tech-tips-tricks/how-to-offset-anchor-tag-link-using-css/ – Partho Oct 07 '17 at 12:07
  • Most likely this will destroy someone's design. But I won't downvote you ;-) – PJ Brunet Jun 02 '18 at 21:13
5

I had the same problem. Here's a jQuery solution

$('a[href^="#"]').on('click',function (e) {
    e.preventDefault();
    var target = this.hash;
    var $trget = $(target);
    // Example: your header is 70px tall.
    var newTop = $trget.offset().top - 70; 
    $('html, body').animate ({
        scrollTop: newTop
    }, 500, 'swing', function () {
        window.location.hash = target;
    }); 
});
PJ Brunet
  • 3,615
  • 40
  • 37
  • The reason for `html, body` is browser compatibility https://stackoverflow.com/a/19738288/722796 – PJ Brunet Jun 05 '18 at 01:32
  • Similar solutions here https://stackoverflow.com/questions/11365091/jquery-scroll-to-anchor-minus-set-amount-of-pixels#12712695 – PJ Brunet Jun 05 '18 at 03:12
  • Since you are applying very strict rules and harsh critics to several of the answers here, I have the following points of grievance here: 1) javascript answer for a css-only question, 2) on top of 1), including a heavy framework for something where it is completely unnecessary 3) faulty code (overriding the window hash with an invalid value) 4) producing duplicate content (the correct thing to do would be to provide a link to the questions in the comment section of the answer) Please at least clean up your code! – Christoph Jun 08 '18 at 15:17
  • @christoph Don't take it personally, my solution works better. Sorry you have a fragile ego, especially over such a weak solution. 1. If you understand English, he does *not* request "CSS only." 2. jQuery is already in the cache of most browsers, there's nothing to download for most people. 3. The rest of your comment is absurd. If you see an error, you can try the "edit" button, but since this jQuery solution works well for many developers, good luck trying to "fix" it. The original code is derived from a popular YouTube video. 4. Swing effect is awesome and you can control the speed. – PJ Brunet Jun 09 '18 at 16:22
  • For being member of the SO community you seem to have surprisingly little knowledge of how the platform works: 1) Tags are there for a reason, 2) downvoting is not there to express your personal opinion but to mark answers that are sloppy, bad or plain out wrong 3) It should be in every users interest to avoid duplicate content. Also, I explained what is wrong in your code, so it's merely an educational move in the spirit of the original meaning of downvoting to have you fix the obvious problems in your code - but yes, I probably would edit the answer of a friendlier user myself instead of DV. – Christoph Jun 12 '18 at 18:48
  • @Christoph You're right about the window hash, I just fixed it. In any case, upon further research, this is a duplicate question. – PJ Brunet Jun 12 '18 at 21:28
  • Hey, now that's a nice jQuery alternative! It even sets the hash of the window location correctly (stealing this for the js part of my answer)! ;-) – Christoph Jun 13 '18 at 19:11
0

Try with window.scrollBy(xnum,ynum);

xnum: How many pixels to scroll by, along the x-axis (horizontal) ynum: How many pixels to scroll by, along the y-axis (vertical)

For example: http://www.w3schools.com/js/tryit.asp?filename=try_dom_window_scrollby

RAN
  • 1,443
  • 3
  • 19
  • 30
  • 1
    If I hadn't a competing post to this question, I would give -1 for referencing w3schools. If you are interested why, check out http://w3fools.com/ – Christoph Jul 16 '12 at 09:15
  • @Christoph Thank you for your valuable information. Here I mentioned only example which shows how it will work, not more than that. – RAN Jul 16 '12 at 09:24
  • 1
    Yah, i just wanted to point out, that w3schools has to be used with extreme care. If you want a better foundation, use [MDN](https://developer.mozilla.org/en/) instead. – Christoph Jul 16 '12 at 09:26
0

After searching through all of the other answers above, the one that worked for me to fix my scrolling anchor issue was the following:

Code:

#idname {
    scroll-margin-top: 20px;
}

Thank you to those above who suggested this answer. You can obviously change the pixel number for the margin. I changed it from 20px to 40px and it works like a charm.

Meloman
  • 3,558
  • 3
  • 41
  • 51