How do I stop a web page from scrolling to the top when a link is clicked that triggers JavaScript?
You need to prevent the default action for the click event (i.e. navigating to the link target) from occurring.
There are two ways to do this.
Option 1: event.preventDefault()
Call the .preventDefault()
method of the event object passed to your handler. If you're using jQuery to bind your handlers, that event will be an instance of jQuery.Event
and it will be the jQuery version of .preventDefault()
. If you're using addEventListener
to bind your handlers, it will be an Event
and the raw DOM version of .preventDefault()
. Either way will do what you need.
Examples:
$('#ma_link').click(function($e) {
$e.preventDefault();
doSomething();
});
document.getElementById('#ma_link').addEventListener('click', function (e) {
e.preventDefault();
doSomething();
})
Option 2: return false;
In jQuery:
Returning false from an event handler will automatically call event.stopPropagation() and event.preventDefault()
So, with jQuery, you can alternatively use this approach to prevent the default link behaviour:
$('#ma_link').click(function(e) {
doSomething();
return false;
});
If you're using raw DOM events, this will also work on modern browsers, since the HTML 5 spec dictates this behaviour. However, older versions of the spec did not, so if you need maximum compatibility with older browsers, you should call .preventDefault()
explicitly. See event.preventDefault() vs. return false (no jQuery) for the spec detail.
You can set your href to #!
instead of #
For example,
<a href="#!">Link</a>
will not do any scrolling when clicked.
Beware! This will still add an entry to the browser's history when clicked, meaning that after clicking your link, the user's back button will not take them to the page they were previously on. For this reason, it's probably better to use the .preventDefault()
approach, or to use both in combination.
Here is a Fiddle illustrating this (just scrunch your browser down until your get a scrollbar):
http://jsfiddle.net/9dEG7/
For the spec nerds - why this works:
This behaviour is specified in the HTML5 spec under the Navigating to a fragment identifier section. The reason that a link with a href of "#"
causes the document to scroll to the top is that this behaviour is explicitly specified as the way to handle an empty fragment identifier:
2. If
fragid
is the empty string, then the indicated part of the document is the top of the document
Using a href of "#!"
instead works simply because it avoids this rule. There's nothing magic about the exclamation mark - it just makes a convenient fragment identifier because it's noticeably different to a typical fragid and unlikely to ever match the id
or name
of an element on your page. Indeed, we could put almost anything after the hash; the only fragids that won't suffice are the empty string, the word 'top', or strings that match name
or id
attributes of elements on the page.
More exactly, we just need a fragment identifier that will cause us to fall through to step 8 in the following algorithm for determining the indicated part of the document from the fragid:
Apply the URL parser algorithm to the URL, and let fragid be the fragment component of the resulting parsed URL.
If
fragid
is the empty string, then the indicated part of the document is the top of the document; stop the algorithm here.Let
fragid bytes
be the result of percent-decodingfragid
.Let
decoded fragid
be the result of applying the UTF-8 decoder algorithm tofragid bytes
. If the UTF-8 decoder emits a decoder error, abort the decoder and instead jump to the step labeledno decoded fragid
.If there is an element in the DOM that has an ID exactly equal to
decoded fragid
, then the first such element in tree order is the indicated part of the document; stop the algorithm here.No decoded fragid: If there is an a element in the DOM that has a name attribute whose value is exactly equal to
fragid
(notdecoded fragid
), then the first such element in tree order is the indicated part of the document; stop the algorithm here.If fragid is an ASCII case-insensitive match for the string
top
, then the indicated part of the document is the top of the document; stop the algorithm here.Otherwise, there is no indicated part of the document.
As long as we hit step 8 and there is no indicated part of the document, the following rule comes into play:
If there is no indicated part ... then the user agent must do nothing.
which is why the browser doesn't scroll.
An easy approach is to leverage this code:
<a href="javascript:void(0);">Link Title</a>
This approach doesn't force a page refresh, so the scrollbar stays in place. Also, it allows you to programmatically change the onclick event and handle client side event binding using jQuery.
For these reasons, the above solution is better than:
<a href="javascript:myClickHandler();">Link Title</a>
<a href="#" onclick="myClickHandler(); return false;">Link Title</a>
where the last solution will avoid the scroll-jump issue if and only if the myClickHandler method doesn't fail.