Make anchor link go some pixels above where it's linked to
JavascriptHtmlCssAnchorJavascript Problem Overview
I'm not sure the best way to ask/search for this question:
When you click on an anchor link, it brings you to that section of the page with the linked-to area now at the VERY TOP of the page. I would like the anchor link to send me to that part of the page, but I would like some space at the top. As in, I don't want it to send me to the linked-to part with it at the VERY TOP, I would like 100 or so pixels of space there.
Does this make sense? Is this possible?
Edited to show code - it's just an anchor tag:
<a href="#anchor">Click me!</a>
<p id="anchor">I should be 100px below where I currently am!</p>
Javascript Solutions
Solution 1 - Javascript
window.addEventListener("hashchange", function () {
window.scrollTo(window.scrollX, window.scrollY - 100);
});
This will allow the browser to do the work of jumping to the anchor for us and then we will use that position to offset from.
EDIT 1:
As was pointed out by @erb, this only works if you are on the page while the hash is changed. Entering the page with a #something
already in the URL does not work with the above code. Here is another version to handle that:
// The function actually applying the offset
function offsetAnchor() {
if(location.hash.length !== 0) {
window.scrollTo(window.scrollX, window.scrollY - 100);
}
}
// This will capture hash changes while on the page
window.addEventListener("hashchange", offsetAnchor);
// This is here so that when you enter the page with a hash,
// it can provide the offset in that case too. Having a timeout
// seems necessary to allow the browser to jump to the anchor first.
window.setTimeout(offsetAnchor, 1); // The delay of 1 is arbitrary and may not always work right (although it did in my testing).
NOTE:
To use jQuery, you could just replace window.addEventListener
with $(window).on
in the examples. Thanks @Neon.
EDIT 2:
As pointed out by a few, the above will fail if you click on the same anchor link two or more times in a row because there is no hashchange
event to force the offset.
This solution is very slightly modified version of the suggestion from @Mave and uses jQuery selectors for simplicity
// The function actually applying the offset
function offsetAnchor() {
if (location.hash.length !== 0) {
window.scrollTo(window.scrollX, window.scrollY - 100);
}
}
// Captures click events of all <a> elements with href starting with #
$(document).on('click', 'a[href^="#"]', function(event) {
// Click events are captured before hashchanges. Timeout
// causes offsetAnchor to be called after the page jump.
window.setTimeout(function() {
offsetAnchor();
}, 0);
});
// Set the offset when entering page with hash present in the url
window.setTimeout(offsetAnchor, 0);
JSFiddle for this example is here
Solution 2 - Javascript
Working only with css you can add a padding to the anchored element (as in a solution above) To avoid unnecessary whitespace you can add a negative margin of the same height:
#anchor {
padding-top: 50px;
margin-top: -50px;
}
I am not sure if this is the best solution in any case, but it works fine for me.
Solution 3 - Javascript
Here's the 2020 answer for this:
#anchor {
scroll-margin-top: 100px;
}
Because it's widely supported!
Solution 4 - Javascript
Even better solution:
<p style="position:relative;">
<a name="anchor" style="position:absolute; top:-100px;"></a>
I should be 100px below where I currently am!
</p>
Just position the <a>
tag with absolute positioning inside of a relatively positioned object.
Works when entering the page or through a hash change within page.
Solution 5 - Javascript
This will work without jQuery and on page load.
(function() {
if (document.location.hash) {
setTimeout(function() {
window.scrollTo(window.scrollX, window.scrollY - 100);
}, 10);
}
})();
Solution 6 - Javascript
Best Solution
<span class="anchor" id="section1"></span>
<div class="section"></div>
<span class="anchor" id="section2"></span>
<div class="section"></div>
<span class="anchor" id="section3"></span>
<div class="section"></div>
<style>
.anchor{
display: block;
height: 115px; /*same height as header*/
margin-top: -115px; /*same height as header*/
visibility: hidden;
}
</style>
Solution 7 - Javascript
This pure CSS solution works for me.
:target:before {
content:"";
display:block;
height:90px; /* fixed header height*/
margin:-90px 0 0; /* negative fixed header height */
}
i found it here -> https://www.wikitechy.com/tutorials/javascript/offsetting-an-html-anchor-to-adjust-for-fixed-header
Here is a fiddle: https://jsfiddle.net/e8o2p3az/
Solution 8 - Javascript
try this code, it has already a smooth animation when clicked the link.
$(document).on('click', 'a[href^="#"]', function (event) {
event.preventDefault();
$('html, body').animate({
scrollTop: $($.attr(this, 'href')).offset().top - 100
}, 500);
});
Solution 9 - Javascript
To link to an element, and then 'position' that element an arbitrary distance from the top of the page, using pure CSS, you'd have to use padding-top
, that way the element is still positioned at the top of the window, but it appears, visibly, to be positioned some distance from the top of the view-port, for example:
<a href="#link1">Link one</a>
<a href="#link2">Link two</a>
<div id="link1">
The first.
</div>
<div id="link2">
The second.
</div>
CSS:
div {
/* just to force height, and window-scrolling to get to the elements.
Irrelevant to the demo, really */
margin-top: 1000px;
height: 1000px;
}
#link2 {
/* places the contents of the element 100px from the top of the view-port */
padding-top: 100px;
}
To use a plain JavaScript approach:
function addMargin() {
window.scrollTo(0, window.pageYOffset - 100);
}
window.addEventListener('hashchange', addMargin);
Solution 10 - Javascript
This should work:
$(document).ready(function () {
$('a').on('click', function (e) {
// e.preventDefault();
var target = this.hash,
$target = $(target);
$('html, body').stop().animate({
'scrollTop': $target.offset().top-49
}, 900, 'swing', function () {
});
console.log(window.location);
return false;
});
});
Just change the .top-49 to what fits with your anchor link.
Solution 11 - Javascript
If you use explicit anchor names such as,
<a name="sectionLink"></a>
<h1>Section<h1>
then in css you can simply set
A[name] {
padding-top:100px;
}
This will work as long as your HREF anchor tags don't also specify a NAME attribute
Solution 12 - Javascript
Eric's answer is great, but you really don't need that timeout. If you're using jQuery, you can just wait for the page to load. So I'd suggest changing the code to:
// The function actually applying the offset
function offsetAnchor() {
if (location.hash.length !== 0) {
window.scrollTo(window.scrollX, window.scrollY - 100);
}
}
// This will capture hash changes while on the page
$(window).on("hashchange", function () {
offsetAnchor();
});
// Let the page finish loading.
$(document).ready(function() {
offsetAnchor();
});
This also gets us rid of that arbitrary factor.
Solution 13 - Javascript
I just found an easy solution for myself. Had an margin-top of 15px
HTML
<h2 id="Anchor">Anchor</h2>
CSS
h2{margin-top:-60px; padding-top:75px;}
Solution 14 - Javascript
The easiest solution:
CSS
#link {
top:-120px; /* -(some pixels above) */
position:relative;
z-index:5;
}
HTML
<body>
<a href="#link">Link</a>
<div>
<div id="link"></div> /*this div should placed inside a target div in the page*/
text
text
text
<div>
</body>
Solution 15 - Javascript
A variant of Thomas' solution: CSS element>element selectors can be handy here:
CSS
.paddedAnchor{
position: relative;
}
.paddedAnchor > a{
position: absolute;
top: -100px;
}
HTML
<a href="#myAnchor">Click Me!</a>
<span class="paddedAnchor"><a name="myAnchor"></a></span>
A click on the link will move the scroll position to 100px above wherever the element with a class of paddedAnchor
is positioned.
Supported in non-IE browsers, and in IE from version 9. For support on IE 7 and 8, a <!DOCTYPE>
must be declared.
Solution 16 - Javascript
Using only css and having no problems with covered and unclickable content before (the point of this is the pointer-events:none):
CSS
.anchored::before {
content: '';
display: block;
position: relative;
width: 0;
height: 100px;
margin-top: -100px;
}
HTML
<a href="#anchor">Click me!</a>
<div style="pointer-events:none;">
<p id="anchor" class="anchored">I should be 100px below where I currently am!</p>
</div>
Solution 17 - Javascript
Put this in style :
.hash_link_tag{margin-top: -50px; position: absolute;}
and use this class in separate div
tag before the links, example:
<div class="hash_link_tag" id="link1"></div>
<a href="#link1">Link1</a>
or use this php code for echo link tag:
function HashLinkTag($id)
{
echo "<div id='$id' class='hash_link_tag'></div>";
}
Solution 18 - Javascript
I know this is a bit late, but I found something very important to put in your code if you are using Bootstrap's Scrollspy. (http://getbootstrap.com/javascript/#scrollspy)
This was driving me nuts for hours.
The offset for scroll spy MUST match the window.scrollY or else you'll run the risk of:
- Getting a weird flicker effect when scrolling
- Youll find that when you click on anchors, youll land in that section, but scroll spy will assume you are a section above it.
var body = $('body');
body.scrollspy({
'target': '#nav',
'offset': 100 //this must match the window.scrollY below or you'll have a bad time mmkay
});
$(window).on("hashchange", function () {
window.scrollTo(window.scrollX, window.scrollY - 100);
});
Solution 19 - Javascript
Based on @Eric Olson solution just modify a little to include the anchor element that I want to go specifically
// Function that actually set offset
function offsetAnchor(e) {
// location.hash.length different to 0 to ignore empty anchor (domain.me/page#)
if (location.hash.length !== 0) {
// Get the Y position of the element you want to go and place an offset
window.scrollTo(0, $(e.target.hash).position().top - 150);
}
}
// Catch the event with a time out to perform the offset function properly
$(document).on('click', 'a[href^="#"]', function (e) {
window.setTimeout(function () {
// Send event to get the target id later
offsetAnchor(e);
}, 10);
});
Solution 20 - Javascript
I just have the same problem. I have a nav posited pixed and i want the angkor to start under the nav. The solution of window.addEventListener...
not work for me because i set my page to be scroll-behavior:smooth
so it set the offset instead scroll to the angkor. the setTimeout()
work if the time is anough for scroll to the end but it still not looking good. so my solution was to add a posited absolute div in the angkor, with height:[the height of the nav]
and bottom:100%
. in this case this div ended in the top of the angkor element, and start at the position where you what the angkor to scroll to. now all what i do is set the angkor link to this absolute div and the wor done :)
html,body{
margin:0;
padding:0;
scroll-behavior:smooth;
}
nav {
height:30px;
width:100%;
font-size:20pt;
text-align:center;
color:white;
background-color:black;
position:relative;
}
#my_nav{
position:fixed;
z-index:3;
}
#fixer_nav{
position:static;
}
#angkor{
position:absolute;
bottom:100%;
height:30px;
}
<nav id="my_nav"><a href="#angkor">fixed position nav<a/></nav>
<nav id="fixer_nav"></nav>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque nec lacus vel eros rutrum volutpat. Cras ultrices enim sit amet odio dictum, eget consectetur mi pellentesque. Sed mollis gravida nulla, eu euismod turpis efficitur id. Integer pretium posuere fringilla. Aenean laoreet, augue non pharetra elementum, lectus massa congue orci, a imperdiet neque enim ut dui. Praesent commodo orci bibendum leo suscipit viverra. Nunc fermentum semper eleifend. Pellentesque suscipit nulla aliquet, egestas lectus sed, egestas dui. Vivamus scelerisque maximus nibh, ac dignissim nunc tempor a. Praesent facilisis non lacus et aliquam. Proin ultricies lacus vitae nibh ullamcorper gravida. Proin elit arcu, convallis eget posuere quis, placerat id augue. Fusce ex risus, tempus nec orci vitae, feugiat faucibus quam. Integer risus metus, ornare et rhoncus vitae, accumsan a urna.
</p>
<nav><div id="angkor"></div>The angkor</nav>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque nec lacus vel eros rutrum volutpat. Cras ultrices enim sit amet odio dictum, eget consectetur mi pellentesque. Sed mollis gravida nulla, eu euismod turpis efficitur id. Integer pretium posuere fringilla. Aenean laoreet, augue non pharetra elementum, lectus massa congue orci, a imperdiet neque enim ut dui. Praesent commodo orci bibendum leo suscipit viverra. Nunc fermentum semper eleifend. Pellentesque suscipit nulla aliquet, egestas lectus sed, egestas dui. Vivamus scelerisque maximus nibh, ac dignissim nunc tempor a. Praesent facilisis non lacus et aliquam. Proin ultricies lacus vitae nibh ullamcorper gravida. Proin elit arcu, convallis eget posuere quis, placerat id augue. Fusce ex risus, tempus nec orci vitae, feugiat faucibus quam. Integer risus metus, ornare et rhoncus vitae, accumsan a urna.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque nec lacus vel eros rutrum volutpat. Cras ultrices enim sit amet odio dictum, eget consectetur mi pellentesque. Sed mollis gravida nulla, eu euismod turpis efficitur id. Integer pretium posuere fringilla. Aenean laoreet, augue non pharetra elementum, lectus massa congue orci, a imperdiet neque enim ut dui. Praesent commodo orci bibendum leo suscipit viverra. Nunc fermentum semper eleifend. Pellentesque suscipit nulla aliquet, egestas lectus sed, egestas dui. Vivamus scelerisque maximus nibh, ac dignissim nunc tempor a. Praesent facilisis non lacus et aliquam. Proin ultricies lacus vitae nibh ullamcorper gravida. Proin elit arcu, convallis eget posuere quis, placerat id augue. Fusce ex risus, tempus nec orci vitae, feugiat faucibus quam. Integer risus metus, ornare et rhoncus vitae, accumsan a urna.
</p>
Solution 21 - Javascript
i was facing the similar issue and i resolved by using following code
$(document).on('click', 'a.page-scroll', function(event) {
var $anchor = $(this);
var desiredHeight = $(window).height() - 577;
$('html, body').stop().animate({
scrollTop: $($anchor.attr('href')).offset().top - desiredHeight
}, 1500, 'easeInOutExpo');
event.preventDefault();
});
Solution 22 - Javascript
I had this same issue, and there's a really quick and simple solution without CSS of JS. Just create a separate unstyled div with an ID like "aboutMeAnchor
and just place it well above the section you actually want to land on.
Solution 23 - Javascript
This is also a good solution. Make a div above the destination div
like this and link the a to this div;
<div class="destination" id="link"></div> /**Needs to be above the destination**/
.destination {
position:absolute;
z-index:-1;
left:0;
margin-top:-100px;/* height of nav*/
}
Solution 24 - Javascript
I was trying to do something similar and what finally worked for me is to add the pseudo element ::before
to the div you want to scroll to. This will add a blank space before the element and the anchor link will scroll to the top of this blank space. For example:
#anchor::before {
content: "";
display: block;
height: 60px;
}
Solution 25 - Javascript
I found a surprising solution to what I think is basically the same issue about the scroll-position of the target of anchor-links.
I have a page which displays a long document with sub-sections whose headers I mark by putting a bookmark dom-element right above them like this:
<div class = "TB_BookMark"
id = "someId"
></div>
<h3 class="TB">Section 1</h3>
The idea is that user can navigate into the document's sub-sections by clicking on links in a side-pane. The first such link should scroll the window to the start of the page. Note that the content of the bookmark-div above is empty meaning it should take no space from the page, and thus navigating to the first link should navigate to the beginning of the page.
The problem was that clicking the first link scrolled the page so that the first section-header was immediately at the top of visible area with no whitespace above it. In other words the page was scrolled down a little from the top. Whereas when the page was loaded or reloaded there is (and should be) a visible white-space margin above the first section-header.
I believe this is the same issue that the original question describes. It seemed there was no way to get the first link to scroll to the very beginning of the page.
Problem is that what you see when you load the page is different from what you see when you click the first link. It doesn't look good when there is no longer a whitespace-margin above the first header, or alternately there would be too big a margin when you load or reload the page.
The way I got it to work is this CSS:
.TB_Bookmark
{ border:solid 0px Transparent; /* Does NOT do what I want. */
border:solid 0.01px Transparent; /* Does what I want. */
}
h3.TB
{ margin-top:12px;
margin-bottom:12px;
}
body
{ margin-top: 0px;
}
All of the above was needed to get it to work except the first border-definition which I left as an example of what does NOT work. The 2nd border-definition of course overrides the first and thus takes effect.
Why "border:solid 0.01px;" works but "border:solid 0px;" does not I don't understand. There should not be a big difference between 0 an 0.01 should there? This solution works both on Chrome and Firefox and stops working on both if I use "border:solid 0px;".
Solution 26 - Javascript
<a href="#anchor">Click me!</a>
<div style="margin-top: -100px; padding-top: 100px;" id="anchor"></div>
<p>I should be 100px below where I currently am!</p>