518

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    |
| ...                             |      | ...                             |
+---------------------------------+      +---------------------------------+
Josh Crozier
  • 233,099
  • 56
  • 391
  • 304
Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • 4
    Related: http://stackoverflow.com/questions/10732690/offsetting-an-html-anchor-to-adjust-for-fixed-header – 0fnt Apr 07 '15 at 09:14
  • 22
    https://css-tricks.com/hash-tag-links-padding/ – ax. Mar 08 '17 at 23:43
  • 6
    @ax YES, that's the best answer. Also check http://nicolasgallagher.com/jump-links-and-viewport-positioning/demo/ inside it. This worked best for me: `.dislocate50px, #bar { padding: 50px 0 0; margin: -50px 0 0; -webkit-background-clip: content-box; background-clip: content-box; }` – flen Apr 09 '17 at 07:47
  • i'm seeking a solution which * works for anchors coming in from same page or other page, * and which adjusts page-down key-press so that content doesn't get clipped by header, * and which allows sib-div footer to scroll up with content-div. Haven't found a solution yet! But i must say, i think the right way would target the whole content div, and not each individual anchor. https://stackoverflow.com/questions/51070758/css-to-make-vertical-scrolling-region-under-fixed-header – johny why Jun 29 '18 at 23:45
  • @ax. CSS-tricks.com has a much better newer article about `scroll-padding-top` here: https://css-tricks.com/fixed-headers-on-page-links-and-overlapping-content-oh-my/ Corresponding answer: https://stackoverflow.com/a/56467997/247696 – Flimm Dec 03 '19 at 11:37
  • I also used https://css-tricks.com/hash-tag-links-padding The only issue I'm having is that anchor references on the same page are inserting whitespace above the H3 realtime on the page(?) – JohnQ Feb 10 '22 at 22:50

38 Answers38

355

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);
Rory O'Kane
  • 29,210
  • 11
  • 96
  • 131
Adrian Garner
  • 5,235
  • 2
  • 23
  • 29
254
html {
  scroll-padding-top: 70px; /* height of sticky header */
}

from: https://css-tricks.com/fixed-headers-on-page-links-and-overlapping-content-oh-my/

Paul Irish
  • 47,354
  • 22
  • 98
  • 132
François Romain
  • 13,617
  • 17
  • 89
  • 123
180

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; }
jameshfisher
  • 34,029
  • 31
  • 121
  • 167
mutttenxd
  • 2,122
  • 1
  • 15
  • 19
  • 25
    @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. – mutttenxd Oct 31 '12 at 15:21
  • Did not work in Chrome v28 due to a WebKit [fixed position bug](http://stackoverflow.com/questions/11258877/fixed-element-disappears-in-chrome) causing header to disappear. [This answer](http://stackoverflow.com/a/14569741/920511) did the trick im my case. – Knickedi Aug 13 '13 at 18:04
  • 2
    MuttenXd.. 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! – zipzit Mar 21 '14 at 16:27
  • Isn'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? – Jake Wilson Feb 10 '15 at 17:50
  • 13
    As suggested in Roy Shoa's answer, add `margin-top: -90px;` to counter the gap created by padding. – Skippy le Grand Gourou Aug 29 '15 at 09:56
  • 45
    I 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. – jimmyplaysdrums Sep 04 '15 at 19:10
  • // handle hashes when page loads // 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(); } }); –  Sep 05 '16 at 06:50
  • I used a little javascript to add and remove padding as needed. `$(function(){ $('#jump').click(function() { $('#id').addClass("anchor"); }); $('.top').click(function() { $('#id').removeClass("anchor"); }); });` – Jarod Thornton Jul 27 '17 at 21:22
  • Note that in nowadays there is single no-hack fully cross-browser solution – `scroll-margin` / `scroll-padding`. – dakur May 20 '21 at 07:39
  • @mutttenxd, I followed the solution which unfortunately didn't help me. Here is the page https://dev-packed-with-purpose.pantheonsite.io/gift-concierge-new/ I'm trying to apply the solution on `Complete Form` cta button. Kindly have a look at the page and suggest some solution. – Pramod Gangadar Aug 18 '22 at 07:30
130

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/

s4y
  • 50,525
  • 12
  • 70
  • 98
Jeremias Erbs
  • 1,925
  • 2
  • 13
  • 7
107

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)

Zoe
  • 27,060
  • 21
  • 118
  • 148
dakur
  • 1,272
  • 1
  • 8
  • 10
51

Official Bootstrap Adopted Answer:

*[id]:before { 
  display: block; 
  content: " "; 
  margin-top: -75px; // Set the Appropriate Height
  height: 75px; // Set the Appropriate Height
  visibility: hidden; 
}

Credits

Merge

blnc
  • 4,384
  • 1
  • 28
  • 42
  • 2
    This solution has a problem when used with list in WebKit. Look: http://stackoverflow.com/questions/39547773/why-alignment-mark-list-is-different-on-webkit-when-using-before-height – Mert S. Kaplan Sep 18 '16 at 15:31
  • Note that this won't work if the target element itself happens to have `display: flex;` or `padding-top`. Do use a dedicated anchor element (like `{leave this empty}`) in those cases. – WoodrowShigeru Aug 20 '19 at 09:13
  • It 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`. – Lideln Kyoku Nov 25 '21 at 19:09
35

The best way that I found to handle this issue is (replace 65px with your fixed element height):

div:target {
  padding-top: 65px; 
  margin-top: -65px;
}

If you do not like to use the target selector you can also do it in this way:

.my-target {
    padding-top: 65px;
    margin-top: -65px;
}

Note: this example will not work if the target element have a backgound color that differant from his parent. for example:

<div style="background-color:red;height:100px;"></div>
<div class="my-target" style="background-color:green;height:100px;"></div>

in this case the green color of my-target element will overwrite his parent red element in 65px. I did not find any pure CSS solution to handle this issue but if you do not have another background color this solution is the best.

Roy Shoa
  • 3,117
  • 1
  • 37
  • 41
20

While some of the proposed solutions work for fragment links (= hash links) within the same page (like a menu link that scrolls down), I found that none of them worked in current Chrome when you want to use fragment links coming in from other pages.

So calling www.mydomain.com/page.html#foo from scratch will NOT offset your target in current Chrome with any of the given CSS solutions or JS solutions.

There is also a jQuery bug report describing some details of the problem.

SOLUTION

The only option I found so far that really works in Chrome is JavaScript that is not called onDomReady but with a delay.

// set timeout onDomReady
$(function() {
    setTimeout(delayedFragmentTargetOffset, 500);
});

// add scroll offset to fragment target (if there is one)
function delayedFragmentTargetOffset(){
    var offset = $(':target').offset();
    if(offset){
        var scrollto = offset.top - 95; // minus fixed header height
        $('html, body').animate({scrollTop:scrollto}, 0);
    }
}

SUMMARY

Without a JS delay solutions will probably work in Firefox, IE, Safari, but not in Chrome.

Jpsy
  • 20,077
  • 7
  • 118
  • 115
  • 3
    I have come to the same conclusion and solution. It seems like every body else is forgetting about external links that use the hashtags. – AJJ Jul 13 '16 at 10:07
  • I 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. – kwiat1990 Apr 07 '17 at 08:07
  • 2
    @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. – Jpsy Apr 07 '17 at 12:13
  • This 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? – Augusto Samamé Barrientos Oct 27 '17 at 16:25
  • @Augusto did you find a solution? I am having the same problem. – Jesse Dec 05 '17 at 16:47
  • Finally - 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. – Brett Jun 22 '19 at 20:51
15

You can now use scroll-margin-top, which is pretty widely adopted.

Simply add the following CSS to the element you want to scroll to:

.element {
  scroll-margin-top: 2em;
}
dutzi
  • 1,880
  • 1
  • 18
  • 23
  • 3
    `scroll-margin-top` has been suggested [earlier in this thread](https://stackoverflow.com/a/55683966/18771) – Tomalak May 23 '20 at 19:08
  • Scroll margin is not applied for scrolls to fragment target...this will not work for anchor tags https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-margin-top – Crystal Gardner Jun 17 '20 at 18:36
  • Works 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. – ujifgc Aug 16 '20 at 23:12
  • It did not work in Safari for a long time, but it is fixed in TP now. See https://github.com/mdn/browser-compat-data/issues/4945#issuecomment-748449872 – dakur Dec 19 '20 at 09:47
  • This should be the accepted answer, works in all but IE – codycustard Jun 23 '21 at 11:49
  • 1
    that's my persona favorite: `:target { scroll-margin-top: 2em; }` – Andreas Jul 09 '21 at 23:00
8

I've got it working easily with CSS and HTML, using the "anchor:before" method mentioned above. I think it works the best, because it doesn't create massive padding between your divs.

.anchor:before {
  content:"";
  display:block;
  height:60px; /* fixed header height*/
  margin:-60px 0 0; /* negative fixed header height */
}

It doesn't seem to work for the first div on the page, but you can counter that by adding padding to that first div.

#anchor-one{padding-top: 60px;}

Here's a working fiddle: http://jsfiddle.net/FRpHE/24/

Eric Wood
  • 579
  • 2
  • 9
  • 22
8

For Chrome/Safari/Firefox you could add a display: block and use a negative margin to compensate the offset, like:

a[name] {
    display: block;
    padding-top: 90px;
    margin-top: -90px;
}

See example http://codepen.io/swed/pen/RrZBJo

T J
  • 42,762
  • 13
  • 83
  • 138
MrSwed
  • 559
  • 8
  • 15
8

I didn't see the option to simply use :target listed as an option. I've found that

:target { margin-top: -100px; padding-top: 100px; }

appears to work in the provided scenario. :target is also compatible with all modern browsers. https://caniuse.com/?search=%3Atarget

  • Very elegant; no messing with the layout. Works for me! I would still add JS to update the height value dynamically. – Kalnode Jan 17 '22 at 20:26
  • Very useful, but in some cases it can lead to the content *below* to overwrite the addressed element (if the element has a fixed height) ☹ – mirabilos Oct 14 '22 at 02:02
7

Just discovered another pure CSS solution that worked like a charme for me!

html {
  scroll-padding-top: 80px; /* height of your sticky header */
}

Found on this site!

jamawe
  • 229
  • 3
  • 12
  • 5
    A few others have discovered this solution before you, see the the answers on page 1, for example https://stackoverflow.com/a/56467997/18771 – Tomalak Dec 10 '19 at 12:40
  • 1
    This doesn't work for scrolling entire page in Edge and Safari (https://bugs.webkit.org/show_bug.cgi?id=179379). – Juliusz Gonera Dec 21 '19 at 01:51
6

You can do this with jQuery:

var offset = $('.target').offset();
var scrollto = offset.top - 50; // fixed_top_bar_height = 50px
$('html, body').animate({scrollTop:scrollto}, 0);
webvitaly
  • 4,241
  • 8
  • 30
  • 48
6

I wasn't having any luck with the answer listed above and ended up using this solution which worked perfectly...

Create a blank span where you want to set your anchor.

<span class="anchor" id="section1"></span>
<div class="section"></div>

And apply the following class:

.anchor {
  display: block;
  height: 115px;       /* same height as header */
  margin-top: -115px;  /* same height as header */
  visibility: hidden;
}

This solution will work even if the sections have different colored backgrounds! I found the solution at this link.

MarredCheese
  • 17,541
  • 8
  • 92
  • 91
  • 2
    Isn't that exactly the same method that [Guillaume Le Mière](https://stackoverflow.com/a/49826477/18771) and a few others have shown in this thread? – Tomalak Nov 02 '18 at 13:36
  • 1
    It 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. – Matt Underwood Nov 02 '18 at 13:42
  • 1
    It is the same solution, but I can understand and follow this one easily. The others didn't give me enough information. – MacroMan May 06 '20 at 08:53
6

I solve my issue with scroll-margin-top property for the anchor element

scroll-margin-top: 100px;

https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-margin-top

Yevhenii
  • 301
  • 1
  • 5
  • 14
6

You could try this:

<style>
h1:target { padding-top: 50px; }
</style>

<a href="#bar">Go to bar</a>

<h1 id="bar">Bar</h1>

Set the top padding value to the actual height of your header. This will introduce a slight extra gap at the top of your header, but it will only be visible when the user jumps to the anchor and then scrolls up. I've made up that solution for my site right now, but it only shows a small fixed bar at the top of the page, nothing too high.

ygoe
  • 18,655
  • 23
  • 113
  • 210
5

It works for me:

HTML LINK to Anchor:

<a href="#security">SECURITY</a>

HTML Anchor:

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

CSS :

.anchor::before {
    content: "";
    display: block;
    margin-top: -50px;
    position: absolute;
}
avila
  • 61
  • 1
  • 2
5

I had a lot of trouble with many of the answers here and elsewhere as my bookmarked anchors were section headers in an FAQ page, so offsetting the header didn't help as the rest of the content would just stay where it was. So I thought I'd post.

What I ended up doing was a composite of a few solutions:

  1. The CSS:

    .bookmark {
        margin-top:-120px;
        padding-bottom:120px; 
        display:block;
    }
    

Where "120px" is your fixed header height (maybe plus some margin).

  1. The bookmark link HTML:

    <a href="#01">What is your FAQ question again?</a>
    
  2. The bookmarked content HTML:

    <span class="bookmark" id="01"></span>
    <h3>What is your FAQ question again?</h3>
    <p>Some FAQ text, followed by ...</p>
    <p>... some more FAQ text, etc ...</p>
    

The good thing about this solution is that the span element is not only hidden, it is essentially collapsed and doesn't pad out your content.

I can't take much credit for this solution as it comes from a swag of different resources, but it worked best for me in my situation.

You can see the actual result here.

Regolith
  • 2,944
  • 9
  • 33
  • 50
SteveCinq
  • 1,920
  • 1
  • 17
  • 22
  • 1
    Thank you very much for sharing! Old as the thread is, there still does not seem to be one canonical solution to this problem. – Tomalak Jul 23 '18 at 07:54
  • 1
    +SteveCinq You read my mind -- I was facing the same issues where shared solutions were not working as predicted. Adding in the `` 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! – twknab Jul 25 '18 at 07:11
  • I 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; – jamheadart Nov 27 '20 at 19:43
4

It feels somewhat hacky to my purist mind but as a css-only solution you can add padding to the active anchored element using the :target selector:

html, body {height:100%; min-height:100%; margin:0;}
body {min-height:200%;}
header {display:inline-block; position:fixed; font-size:1.5em; height:100px; top:0; left:0; right:0; line-height:100px; background:black; text-align:center;}
header a {color:#fff;}
section {padding:30px; margin:20px;}
section:first-of-type, section:target {padding-top:130px;}
<header><a href="#one">#One</a> <a href="#two">#two</a> <a href="#three">#three</a></header>
<section id="one"><h1>One</h1>Aenean lacinia bibendum nulla sed consectetur. Nullam id dolor id nibh ultricies vehicula ut id elit. Integer posuere erat a ante venenatis dapibus posuere velit aliquet.</section>
<section id="two"><h1>Two</h1>Aenean lacinia bibendum nulla sed consectetur. Nullam id dolor id nibh ultricies vehicula ut id elit. Integer posuere erat a ante venenatis dapibus posuere velit aliquet.</section>
<section id="three"><h1>Three</h1>Aenean lacinia bibendum nulla sed consectetur. Nullam id dolor id nibh ultricies vehicula ut id elit. Integer posuere erat a ante venenatis dapibus posuere velit aliquet.</section>
Moob
  • 14,420
  • 1
  • 34
  • 47
  • this just creates a huge space of padding above the the targeted section which you easily notice when scrolling back up. very hacky solution – Puddle Aug 02 '21 at 17:24
4

I'm using @Jpsy's answer, but for performance reasons I'm only setting the timer if the hash is present in the URL.

$(function() {
      // Only set the timer if you have a hash
      if(window.location.hash) {
        setTimeout(delayedFragmentTargetOffset, 500);
      }
  });

function delayedFragmentTargetOffset(){
      var offset = $(':target').offset();
      if(offset){
          var scrollto = offset.top - 80; // minus fixed header height
          $('html, body').animate({scrollTop:scrollto}, 0);
          $(':target').highlight();
      }
  };
sandre89
  • 5,218
  • 2
  • 43
  • 64
3

I found I had to use both MutttenXd's and Badabam's CSS solutions together, as the first did not work in Chrome and the second did not work in Firefox:

a.anchor { 
  padding-top: 90px;
}

a.anchor:before { 
  display: block;
  content: "";
  height: 90px;
  margin-top: -90px;
}

<a class="anchor" name="shipping"></a><h2>Shipping (United States)</h2>
...
tshalif
  • 2,277
  • 2
  • 15
  • 10
3

The way that I find being the cleanest is the following one :

  #bar::before {
    display: block;
    content: " ";
    margin-top: -150px;
    height: 150px;
    visibility: hidden;
    pointer-events: none;
  }
glemiere
  • 4,790
  • 7
  • 36
  • 60
2

A minimally intrusive approach using jQuery:

Link:

<a href="#my-anchor-1" class="anchor-link">Go To Anchor 1</a>

Content:

<h3 id="my-anchor-1">Here is Anchor 1</a>

Script:

$(".anchor-link").click(function() {
    var headerHeight = 120;
    $('html, body').stop(true, true).animate({
        scrollTop: $(this.hash).offset().top - headerHeight
    }, 750);
    return false;
});

By assigning the anchor-link class to the links, the behaviour of other links (like accordion or tab controls) are not affected.

The question doesn't want javascript but the other more popular question is closed because of this one and I couldn't answer there.

Huseyin Yagli
  • 9,896
  • 6
  • 41
  • 50
2

I needed something that works for inbound links, links on page, AND that can be targeted by JS so the page can respond to changes in the header height

HTML

<ul>
  <li><a href="#ft_who">Who?</a></li>
  <li><a href="#ft_what">What?</a></li>
  <li><a href="#ft_when">When?</a></li>
</ul>
...
<h2 id="ft_who" class="fragment-target">Who?</h2> 
...
<a href="#">Can I be clicked?</a>
<h2 id="ft_what" class="fragment-target">What?</h2>
...
<h2 id="ft_when" class="fragment-target">When?</h2> 

CSS

.fragment-target {
    display: block;
    margin-top: -HEADER_HEIGHTpx;
    padding-top: HEADER_HEIGHTpx;
    z-index: -1;
}

The z-index: -1 allows links in the 'padding area' above a fragment-target to still be clickable, as commented by @MuttenXd on his answer

I haven't found an issue yet in IE 11, Edge 15+, Chrome 38+, FF 52+, or Safari 9.1+

Arth
  • 12,789
  • 5
  • 37
  • 69
1

Here is a complete jquery solution that will work in IE:

Suppose the navigation bar elements are something like this:

<ul>
    <li><a class="navigation" href="/#contact-us">Contact us</a></li>
    <li><a class="navigation" href="/#about-us">About us</a></li>
</ul>

You can use the following jquery snippet to offset the scroll:

$(function() {
    $("a.navigation").click(function(event) {
        event.preventDefault();
        offSetScroll($(this));
    });
    offSetScrollFromLocation(window.location.href.toLowerCase());
});

function offSetScroll(anchor) {
    var href = anchor.attr("href");
    offSetScrollFromLocation(href);
}

function offSetScrollFromLocation(href) {
    //Change this according to the height of the navigation bar
    var fromTop = 250;
    if(href.indexOf("#")<=0)
        return;
    var hash=href.substring(href.indexOf("#"));
    var targetOffset = $(hash).offset().top-fromTop;
    $('html, body').animate({scrollTop: targetOffset}, 400, function(e) {

    });
}
Thunder
  • 2,994
  • 1
  • 24
  • 19
  • I 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 http://stackoverflow.com/questions/10732690/offsetting-an-html-anchor-to-adjust-for-fixed-header and on http://stackoverflow.com/questions/10732690/offsetting-an-html-anchor-to-adjust-for-fixed-header 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 Feb 14 '17 at 14:29
  • 1
    To 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. – Jeffrey Simon Feb 14 '17 at 14:37
1

Implemented using :before worked great until we realized that the pseudo element was actually covering and blocking pointer events that sat within the pseudo element's area. Using something like pointer-events: none on the :before or even directly on the anchor had no affect.

What we ended up doing was making the anchor's positioning absolute and then adjusting it's position to be the offset/height of the fixed area.

Offset Anchor without Blocking Pointer Events

.section-marker {

    position: absolute;
    top: -300px;
}

The value with this is that we're not blocking any elements that might fall within those 300px. The downside is that grabbing that element's position from Javascript needs to take into account that offset so any logic there had to be adjusted.

skabob11
  • 1,306
  • 1
  • 10
  • 19
1

This is how I got it to finally go to the proper place when you click on the navigation. I added an event handler for the navigation clicks. Then you can just use "scrollBy" to move up on the offset.

var offset = 90;

 $('.navbar li a').click(function(event) {
    event.preventDefault();
    $($(this).attr('href'))[0].scrollIntoView();
    scrollBy(0, -offset);
 });
Mile Mijatović
  • 2,948
  • 2
  • 22
  • 41
1

I created a div with a few line breaks and gave that the id, I then put the code I wanted to show underneath. The link would then take you to the space above the image and the header would no longer be in the way:

<a href="#image">Image</a>
<div id="image"><br><br></div>
<img src="Image.png">

Of course, you can change the number of line breaks to suit your needs. This worked perfectly for me, not sure if there are any problems though, I am still learning HTML.

Anonymous
  • 11
  • 2
  • Pragmatic, 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 `
    ` tags before each heading.
    – Tomalak Dec 22 '18 at 15:33
1
<div style="position:relative; top:-45px;">
    <a name="fragment"> </a>
</div>

This code should do the trick. Swap out 45px for the height of your header bar.

EDIT: If using jQuery is an option, I've also been successful using jQuery.localScroll with an offset value set. The offset option is a part of jQuery.scrollTo, which jQuery.localScroll is built upon. A demo is available here: http://demos.flesler.com/jquery/scrollTo/ (second window, under 'offset')

fourk
  • 2,224
  • 1
  • 16
  • 10
  • this 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. – vlad saling Aug 25 '11 at 15:03
  • Shame this is so messy, means you can't just use IDs. Would love to find a solution, I have tried all sorts of things. – theorise Sep 21 '11 at 15:49
1

Used this script

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

    $('html, body').animate({
        scrollTop: $($.attr(this, 'href')).offset().top -140
    }, 1000);
});
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
1

I think this approach is more useful:

<h2 id="bar" title="Bar">Bar</h2>

[id]:target {
    display: block;
    position: relative;
    top: -120px;
    visibility: hidden;
}

[id]:target::before {
    content: attr(title);
    top: 120px;
    position: relative;
    visibility: visible;
}
Enes Sahin
  • 11
  • 1
0

Add a class with a "paddingtop" so it works ;)

<h1><a class="paddingtop">Bar</a></h1>

And for css you have:

.paddingtop { 
   padding-top: 90px; 
}
MarredCheese
  • 17,541
  • 8
  • 92
  • 91
MAESTRO_DE
  • 433
  • 2
  • 17
0

I use this method because for some reason, none of the other solutions proposed actually worked for me. I promise I tried.

section {
   position: relative;
   border-top: 52px solid transparent; /* navbar height +2 */
   margin: -30px 0 0;
   -webkit-background-clip: padding-box;
   -moz-background-clip: padding;
   background-clip: padding-box;
}

section:before {
   content: "";
   position: absolute;
   top: -2px;
   left: 0;
   right: 0;
   border-top: 2px solid transparent;
}

Replace section by a class if you prefer.

source: Jump links and viewport positioning

  • Tested on Firefox 45 and Chrome 52.
  • bootstrap version: 3.3.7

For those who do not believe me I kindly prepared a jsfiddle with the solution in it: SOLUTION

scavenger
  • 414
  • 4
  • 10
0

CSS trick will be a workaround. A proper solution which will work in all scenario can be implemented using jQuery.

Refer to https://codepen.io/pikeshmn/pen/mMxEdZ

Approach: We get the height of fixed nav using document.getElementById('header').offsetHeight And offset the scroll to this value.

var jump=function(e){  

e.preventDefault();                        //prevent "hard" jump
  var target = $(this).attr("href");       //get the target

      //perform animated scrolling
      $('html,body').animate(
        {
          scrollTop: $(target).offset().top - document.getElementById('header').offsetHeight - 5  //get top-position of target-element and set it as scroll target
        },1000,function()                  //scrolldelay: 1 seconds
        {
          location.hash = target;          //attach the hash (#jumptarget) to the pageurl
        });
      }

  $(document).ready(function()
  {
    $('a[href*="#"]').bind("click", jump); //get all hrefs
    return false;
  });

P.S:

  • It includes a nice 5 pixels difference between header and target
  • Scroll effect is not hard, rather smooth; Smooth Scrolling
Pikesh Prasoon
  • 377
  • 2
  • 8
0

A very simple CSS only answer is to put this at the top of your stylesheet:

a{padding-top: 90px;}
a:link{padding-top: unset;}

The first style regulates all anchor tags where the second styles anchor tags with a hyperlink.

Note: This currently works in Firefox and Edge but nor in Chrome.

Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135
0

Simple and easy way by adding scroll-padding-top property to solve this problem, just add this code into your CSS file and see the magic.

for dynamic height and gap variables

html {   
    --headerHeight:79px;   
    --gap:40px;   
    scroll-padding-top: calc(var(--headerHeight) + var(--gap)); 
}

for fixed gap value

html { 
    scroll-padding-top: 120px; 
}
Aftab Ahmed
  • 665
  • 9
  • 17
-1

The top answer here created a 60px-high tag that was masking other links that stopped working as a result. I found this solution working without side-effects.

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

a.anchor {
    display: block;
    position: relative;
    top: -60px;
    visibility: hidden;
}
thisismydesign
  • 21,553
  • 9
  • 123
  • 126