535

I have a couple of hyperlinks on my page. A FAQ that users will read when they visit my help section.

Using Anchor links, I can make the page scroll towards the anchor and guide the users there.

Is there a way to make that scrolling smooth?

But notice that he's using a custom JavaScript library. Maybe jQuery offers somethings like this baked in?

Fred Rocha
  • 460
  • 3
  • 15
Only Bolivian Here
  • 32,571
  • 60
  • 151
  • 250

28 Answers28

1261

Update April 2018: There's now a native way to do this:

document.querySelectorAll('a[href^="#"]').forEach(anchor => {
    anchor.addEventListener('click', function (e) {
        e.preventDefault();

        document.querySelector(this.getAttribute('href')).scrollIntoView({
            behavior: 'smooth'
        });
    });
});

This is currently only supported in the most bleeding edge browsers.


For older browser support, you can use this jQuery technique:

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

    $('html, body').animate({
        scrollTop: $($.attr(this, 'href')).offset().top
    }, 500);
});

And here's the fiddle: http://jsfiddle.net/9SDLw/


If your target element does not have an ID, and you're linking to it by its name, use this:

$('a[href^="#"]').click(function () {
    $('html, body').animate({
        scrollTop: $('[name="' + $.attr(this, 'href').substr(1) + '"]').offset().top
    }, 500);

    return false;
});

For increased performance, you should cache that $('html, body') selector, so that it doesn't run every single time an anchor is clicked:

var $root = $('html, body');

$('a[href^="#"]').click(function () {
    $root.animate({
        scrollTop: $( $.attr(this, 'href') ).offset().top
    }, 500);

    return false;
});

If you want the URL to be updated, do it within the animate callback:

var $root = $('html, body');

$('a[href^="#"]').click(function() {
    var href = $.attr(this, 'href');

    $root.animate({
        scrollTop: $(href).offset().top
    }, 500, function () {
        window.location.hash = href;
    });

    return false;
});
Joseph Silber
  • 193,614
  • 53
  • 339
  • 276
  • 11
    This seems to remove the #extension from the URL, breaking the back function. Is there a way around this? – Fletch Jan 22 '13 at 18:46
  • 1
    @Fletch - I added a solution to that to the answer. – Joseph Silber Jan 22 '13 at 18:50
  • Could you please fix a typo in the third line of your last snippet: should be attr, not att. – Anton Daneyko Feb 23 '13 at 20:53
  • Btw, the URL is updated, but the back button still does not bring me back to where I left from. Is there a way to deal with that? – Anton Daneyko Feb 23 '13 at 21:06
  • @mezhaka - Check out Ben Alman's [BBQ plugin](http://benalman.com/projects/jquery-bbq-plugin/). – Joseph Silber Feb 24 '13 at 06:42
  • 3
    @JosephSilber shouldn't that be `scrollTop: $(this.hash).offset().top` instead of `scrollTop: $(this.href).offset().top`? – Gregory Pakosz Mar 30 '13 at 22:56
  • @GregoryPakosz - `HTMLAnchorElement.hash` is part of the HTML5 spec, so I don't know how good support is in older browsers. – Joseph Silber Apr 03 '13 at 14:56
  • fair enough, yet using `$(this.href)` in place of `$(this).attr('href')` doesn't work http://jsfiddle.net/tbedG/ – Gregory Pakosz Apr 03 '13 at 15:11
  • also note that `$(this.hash)` works in IE6 with Modernizr (didn't test without it) – Gregory Pakosz Apr 03 '13 at 15:14
  • @GregoryPakosz - My bad. `this.href` uses the `href` property instead of the `href` attribute (which can be accessed via `this.getAttribute('href')`). In some browsers, the property has the current URL prepended to the hash. I updated the code above to use the attribute instead. Here's your fiddle: http://jsfiddle.net/tbedG/1/ – Joseph Silber Apr 03 '13 at 15:18
  • @GregoryPakosz - `$.attr(this, 'href')` is a little more performant than `$(this).attr('href')`, since it [just uses the static method](http://jsperf.com/jquery-method-vs-static). – Joseph Silber Apr 03 '13 at 15:23
  • I just implemented the code from the last block and it's working perfectly. However I need to add an addtional 72px to the offset so that content isn't being placed under the navigation fixed to the top. I've tried a couple of things, but I keep breaking the code. Any help appreciated. – CreateSean Jul 18 '13 at 20:26
  • 5
    @CreateSean - `scrollTop: $(href).offset().top - 72` – Joseph Silber Jul 19 '13 at 01:45
  • Hey thanks a lot for the code but can you please have a look at [This Fiddle](http://jsfiddle.net/plsbugmenot/L677v/) and tell me what i'm doing wrong, because for some reason i'm not able to get mine to work. Its the exact code except that im using jquery 1.11.0 – Nikhil Nanjappa Mar 21 '14 at 12:50
  • If you need to use absolute links, like http://example.com/#myAnchor, this is the way to go: `jQuery('a').click(function() { var href = jQuery.attr(this, 'href'); var targetID = href.substr(href.indexOf("#") + 1); jQueryroot.animate({ scrollTop: jQuery("#" + targetID).offset().top }, 850, function() { window.location.hash = targetID; }); return false; });` – Pablo S G Pacheco Jul 12 '14 at 04:09
  • 6
    I'd argue that caching the `html, body` object here is unnecessary, running a selector once per click is not really that much. –  Jun 25 '16 at 14:09
  • I tried so hard to apply this to my site but something is off. Could anyone give my jsfiddle a look? Trying to make top right nav buttons scroll to their prospective areas on page (might have to widen preview window to make them appear). They are being targeted properly (other links function normally, only these lack default behavior), just won't scroll down. I thought problem might be due to my CSS parallax, but smooth scrolling still doesn't work when that's disabled. Going crazy! :P https://jsfiddle.net/KyleVassella/mr4chqdg/ – Kyle Vassella Dec 09 '16 at 17:22
  • ^^^Solved my problem in the comment above. For anyone who can't get this to work, my problem was in my CSS. I found the following response on another Stack question which solved my problem: **...the problem with your code, it is your css. In your css you have** `html, body { overflow-x: hidden; }` **change that to this:** `body { overflow-x: hidden; }` – Kyle Vassella Dec 10 '16 at 10:51
  • 1
    This broke my entire site because it affects ALL links. – Dikeneko Apr 14 '17 at 14:52
  • @Fletch remove the event.preventDefault(); – jairhumberto Sep 29 '17 at 00:16
  • @Dikeneko - Change the selector to `a[href^="#"]`, which only selects anchors that start with a `#`. I updated the answer. – Joseph Silber Sep 29 '17 at 12:51
  • @Dineneko Or use a selector class to specify which anchors it applies to `a.custom-scroll[href^="#"]`. Then apply the class 'custom-scroll' to any anchor you want to use this on. – Jon Hulka Apr 06 '18 at 01:51
  • 3
    First solution is the best and most modern, you can use this polyfill to support this behavior on old browsers with this [polyfill](https://github.com/iamdustan/smoothscroll) – Efe Apr 11 '18 at 11:24
  • What if I **also** need to detect clicks on links that should scroll to the top? Example: `Top` – Andres SK Nov 08 '18 at 06:49
  • @AndresSK - `if (this.getAttribute('href') == '#') // scroll to document.body` – Joseph Silber Nov 08 '18 at 16:48
  • Sorry to chime in. The new native way of doing this works wonders on desktop but not on mobile devices, at least iOS. I'm guessing the polyfilll would help with that? Just want to confirm. Thanks in advance, fellas. – Robert Molina Nov 11 '18 at 18:25
  • When adding an offset `window.location.hash = href;` caused the page to jerk/glitch back and ignore the offset. Fixed it by placing `window.location.hash = href;` before `returning false` instead of in the animate function. – StefanJM May 29 '19 at 00:52
  • This works. But preventDefault will prevent keyboard focus from moving to target. It seems to work fine without preventDefault. – Kjetil Hansen Jun 18 '19 at 10:24
  • NOT WORKING both solutions. queryselector function returns an error "Uncaught TypeError: Cannot read property 'scrollIntoView' of null" while offset().top function returns "Uncaught TypeError: Cannot read property 'top' of undefined" errors. I am using windows 10, browser chrome version 78.0.3904.70 (Official Build) (64-bit) any other solution?? – Kamlesh Nov 01 '19 at 13:25
  • @JosephSilber Your last example works but I get a bug without any error in the console. I spammed the anchors and tried every situation I could think of. I am using two anchors (go to top and go to bottom) and they work fine together but if I scroll to bottom and click go to top and do it twice, the second time it won't work. Somehow I can "reset it" by clicking the anchor go to bottom and go to top again for it to work. – pensum Apr 12 '20 at 06:32
  • ["Smooth scroll into view not working in >= Chrome 52"](https://github.com/iamdustan/smoothscroll/issues/28) – Marco Panichi Apr 24 '20 at 09:38
  • 1
    This solution works. Unfortunately, I had to use the jQuery version for smooth scrolling to work properly in Safari as of August 2020. – Brandon Clapp Aug 22 '20 at 17:48
  • I like this solution, but extended the selector to listen to links that don't ONLY contain a "#", because those are often used for animations or AJAX or so, and they cannot be used to scroll anywhere, too. So my first line (non-deprecated jQuery code) now looks like this: `$('a[href^="#"]').not('[href$="#"]').on("click", function() {` – Jan Feb 21 '21 at 19:26
  • After so many hours spent, one of the solutions worked. I still don't get how such a basic thing was not working and I was forced to use jQuery to solve it. Anyway, thanks a lot, really helpful! – John M. Apr 21 '21 at 22:15
171

The correct syntax is:

//Smooth scrolling with links
$('a[href*=\\#]').on('click', function(event){     
    event.preventDefault();
    $('html,body').animate({scrollTop:$(this.hash).offset().top}, 500);
});

// Smooth scrolling when the document is loaded and ready
$(document).ready(function(){
  $('html,body').animate({scrollTop:$(location.hash).offset().‌​top}, 500);
});

Simplifying: DRY

function smoothScrollingTo(target){
  $('html,body').animate({scrollTop:$(target).offset().​top}, 500);
}
$('a[href*=\\#]').on('click', function(event){     
    event.preventDefault();
    smoothScrollingTo(this.hash);
});
$(document).ready(function(){
  smoothScrollingTo(location.hash);
});

Explanation of href*=\\#:

  • * means it matches what contains # char. Thus only match anchors. For more about the meaning of this, see here
  • \\ is because the # is a special char in css selector, so we have to escape it.
T.Todua
  • 44,747
  • 17
  • 195
  • 185
Andres Separ
  • 2,508
  • 1
  • 13
  • 14
118

The new hotness in CSS3. This is a lot easier than every method listed on this page and requires no Javascript. Just enter the below code into you css and all of a sudden links point to locations inside you own page will have a smooth scrolling animation.

html{scroll-behavior:smooth}

After that any links pointed towards a div will smoothly glide over to those sections.

<a href="#section">Section1</a>

Edit: For those confused about the above a tag. Basically it's a link that's clickable. You can then have another div tag somewhere in your web page like

<div classname="section">content</div>

In this regard the a link will be clickable and will go to whatever #section is, in this case it's our div we called section.

BTW, I spent hours trying to get this to work. Found the solution in some obscure comments section. It was buggy and wouldn't work in some tags. Didn't work in the body. It finally worked when I put it in html{} in the CSS file.

Cristian Reyes
  • 1,283
  • 1
  • 10
  • 11
39

Only CSS

html {
    scroll-behavior: smooth !important;
}

All you need to add only this. Now your internal links scrolling behavior will be smooth like a stream-flow.

Programmatically: Something extra and advanced

// Scroll to specific values
// window.scrollTo or
window.scroll({
  top: 1000, 
  left: 0, 
  behavior: 'smooth'
});

// Scroll certain amounts from current position 
window.scrollBy({ 
  top: 250, // could be negative value
  left: 0, 
  behavior: 'smooth' 
});

// Scroll to a certain element
document.getElementById('el').scrollIntoView({
  behavior: 'smooth'
})

Note: All latest browsers (Opera, Chrome, Firefox etc) supports this feature.

for detail understanding, read this article

WasiF
  • 15,431
  • 9
  • 81
  • 100
23
$('a[href*=#]').click(function(event){
    $('html, body').animate({
        scrollTop: $( $.attr(this, 'href') ).offset().top
    }, 500);
    event.preventDefault();
});

this worked perfect for me

Philipp Sander
  • 9,615
  • 3
  • 41
  • 75
21

I'm surprised no one has posted a native solution that also takes care of updating the browser location hash to match. Here it is:

let anchorlinks = document.querySelectorAll('a[href^="#"]')
 
for (let item of anchorlinks) { // relitere 
    item.addEventListener('click', (e)=> {
        let hashval = item.getAttribute('href')
        let target = document.querySelector(hashval)
        target.scrollIntoView({
            behavior: 'smooth',
            block: 'start'
        })
        history.pushState(null, null, hashval)
        e.preventDefault()
    })
}

See tutorial: http://www.javascriptkit.com/javatutors/scrolling-html-bookmark-javascript.shtml

For sites with sticky headers, scroll-padding-top CSS can be used to provide an offset.

Lee Floyd
  • 30
  • 6
coco puffs
  • 850
  • 8
  • 8
  • 2
    I like this answer the most. However afais there is no way to provide an offset. As would be needed in the case of a fixed header. – bskool Oct 23 '18 at 09:31
  • 1
    Unfotunately, the same poor support as for CSS scroll-behavior property: https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior#Browser_compatibility – Dmitry Nevzorov Feb 07 '19 at 16:22
  • 1
    Love it, works out of the box with history and all :) – avocade Jan 12 '21 at 09:41
12

I suggest you to make this generic code :

$('a[href^="#"]').click(function(){

var the_id = $(this).attr("href");

    $('html, body').animate({
        scrollTop:$(the_id).offset().top
    }, 'slow');

return false;});

You can see a very good article here : jquery-effet-smooth-scroll-defilement-fluide

sarah bouyer
  • 121
  • 1
  • 2
7
$(function() {
  $('a[href*=#]:not([href=#])').click(function() {
    if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
      var target = $(this.hash);
      target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
      if (target.length) {
        $('html,body').animate({
          scrollTop: target.offset().top
        }, 1000);
        return false;
      }
    }
  });
});

Official: http://css-tricks.com/snippets/jquery/smooth-scrolling/

therealrootuser
  • 7,249
  • 6
  • 27
  • 43
KingRider
  • 1,806
  • 21
  • 20
  • 1
    this only seems to work for inner page anchor links, but anchor links from other pages do not work, for example http://website.com/about-us/#who-we-are – rainerbrunotte May 30 '17 at 07:37
6

There are already a lot of good answers here - however they are all missing the fact that empty anchors have to be excluded. Otherwise those scripts generate JavaScript errors as soon as an empty anchor is clicked.

In my opinion the correct answer is like this:

$('a[href*=\\#]:not([href$=\\#])').click(function() {
    event.preventDefault();

    $('html, body').animate({
        scrollTop: $($.attr(this, 'href')).offset().top
    }, 500);
});
Blackbam
  • 12,200
  • 19
  • 71
  • 117
  • 1
    plus, you need to take into consideretion when you click on a hashed link from a different url, so there will be a lot of `window.location....` and `$(this).attr('href').substring(...)` handling along – user151496 May 11 '21 at 12:42
5

Using JQuery:

$('a[href*=#]').click(function(){
  $('html, body').animate({
    scrollTop: $( $.attr(this, 'href') ).offset().top
  }, 500);
  return false;
});
Jonatas Walker
  • 11,553
  • 5
  • 43
  • 74
brequinn
  • 51
  • 1
  • 2
5

There is a css way of doing this using scroll-behavior. Add the following property.

    scroll-behavior: smooth;

And that is it. No JS required.

a {
  display: inline-block;
  width: 50px;
  text-decoration: none;
}
nav, scroll-container {
  display: block;
  margin: 0 auto;
  text-align: center;
}
nav {
  width: 339px;
  padding: 5px;
  border: 1px solid black;
}
scroll-container {
  display: block;
  width: 350px;
  height: 200px;
  overflow-y: scroll;
  scroll-behavior: smooth;
}
scroll-page {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  font-size: 5em;
}
<nav>
  <a href="#page-1">1</a>
  <a href="#page-2">2</a>
  <a href="#page-3">3</a>
</nav>
<scroll-container>
  <scroll-page id="page-1">1</scroll-page>
  <scroll-page id="page-2">2</scroll-page>
  <scroll-page id="page-3">3</scroll-page>
</scroll-container>

PS: please check the browser compatibility.

Santosh
  • 838
  • 12
  • 10
5

There is native support for smooth scrolling on hash id scrolls.

html {
  scroll-behavior: smooth;
}

You can take a look: https://www.w3schools.com/howto/howto_css_smooth_scroll.asp#section2

4

The answer given works but disables outgoing links. Below a version with an added bonus ease out (swing) and respects outgoing links.

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

        var target = this.hash;
        var $target = $(target);

        $('html, body').stop().animate({
            'scrollTop': $target.offset().top
        }, 900, 'swing', function () {
            window.location.hash = target;
        });
    });
});
Thomas Kekeisen
  • 4,192
  • 4
  • 32
  • 50
Rick
  • 41
  • 3
  • 1
    +1 for the `stop()` however the url crumb doesn't work as expected: the Back button doesn't bring back, this is because when the crumb is set in the url after the animation completes. It's better without a crumb in the url, for example that's how airbnb does. – Eric Aug 22 '17 at 14:32
4

HTML

<a href="#target" class="smooth-scroll">
    Link
</a>
<div id="target"></div>

or With absolute Full URL

<a href="https://somewebsite.com/#target" class="smooth-scroll">
    Link
</a>
<div id="target"></div>

jQuery

$j(function() {
    $j('a.smooth-scroll').click(function() {
        if (
                window.location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '')
            &&  window.location.hostname == this.hostname
        ) {
            var target = $j(this.hash);
            target = target.length ? target : $j('[name=' + this.hash.slice(1) + ']');
            if (target.length) {
                $j('html,body').animate({
                    scrollTop: target.offset().top - 70
                }, 1000);
                return false;
            }
        }
    });
});
Jasmeet Singh
  • 487
  • 4
  • 6
4

Modern browsers are a little faster these days. A setInterval might work. This function work well in Chrome and Firefox these days.(A little slow in safari, didn't bother with IE)

function smoothScroll(event) {
    if (event.target.hash !== '') { //Check if tag is an anchor
        event.preventDefault()
        const hash = event.target.hash.replace("#", "")
        const link = document.getElementsByName(hash) 
        //Find the where you want to scroll
        const position = link[0].getBoundingClientRect().y 
        let top = 0

        let smooth = setInterval(() => {
            let leftover = position - top
            if (top === position) {
                clearInterval(smooth)
            }

            else if(position > top && leftover < 10) {
                top += leftover
                window.scrollTo(0, top)
            }

            else if(position > (top - 10)) {
                top += 10
                window.scrollTo(0, top)
            }

        }, 6)//6 milliseconds is the faster chrome runs setInterval
    }
}
littleboots
  • 111
  • 3
3

Adding this:

function () {
    window.location.hash = href;
}

is somehow nullifying the vertical offset

top - 72

in Firefox and IE, ut not in Chrome. Basically, the page scrolls smoothly to the point at which it should stop based upon the offset, but then jumps down to where the page would go without the offset.

It does add the hash to the end of the url, but pressing back does not take you back to the top, it just removes the hash from the url and leaves the viewing window where it sits.

Here is the full js I am using:

var $root = $('html, body');
$('a').click(function() {
    var href = $.attr(this, 'href');
    $root.animate({
        scrollTop: $(href).offset().top - 120
    }, 500, function () {
        window.location.hash = href;
    });
    return false;
});
Ashkan Mobayen Khiabani
  • 30,915
  • 26
  • 90
  • 147
Reid
  • 31
  • 2
3

This solution will also work for the following URLs, without breaking anchor links to different pages.

http://www.example.com/dir/index.html
http://www.example.com/dir/index.html#anchor

./index.html
./index.html#anchor

etc.

var $root = $('html, body');
$('a').on('click', function(event){
    var hash = this.hash;
    // Is the anchor on the same page?
    if (hash && this.href.slice(0, -hash.length-1) == location.href.slice(0, -location.hash.length-1)) {
        $root.animate({
            scrollTop: $(hash).offset().top
        }, 'normal', function() {
            location.hash = hash;
        });
        return false;
    }
});

I haven't tested this in all browsers, yet.

Midas
  • 6,704
  • 5
  • 30
  • 51
3

This will make it easy to allow jQuery to discern your target hash and know when and where to stop.

$('a[href*="#"]').click(function(e) {
    e.preventDefault();
    var target = this.hash;
    $target = $(target);

    $('html, body').stop().animate({
        'scrollTop': $target.offset().top
    }, 900, 'swing', function () {
        window.location.hash = target;
    });
});
PanicBus
  • 548
  • 1
  • 7
  • 16
3
$("a").on("click", function(event){
    //check the value of this.hash
    if(this.hash !== ""){
        event.preventDefault();

        $("html, body").animate({scrollTop:$(this.hash).offset().top}, 500);

        //add hash to the current scroll position
        window.location.hash = this.hash;

    }



});
Abk
  • 1,669
  • 1
  • 17
  • 25
3

Tested and Verified Code

<script>
jQuery(document).ready(function(){
// Add smooth scrolling to all links
jQuery("a").on('click', function(event) {

// Make sure this.hash has a value before overriding default behavior
if (this.hash !== "") {
  // Prevent default anchor click behavior
  event.preventDefault();

  // Store hash
  var hash = this.hash;

  // Using jQuery's animate() method to add smooth page scroll
  // The optional number (800) specifies the number of milliseconds it takes to scroll to the specified area
  jQuery('html, body').animate({
    scrollTop: jQuery(hash).offset().top
  }, 800, function(){

    // Add hash (#) to URL when done scrolling (default click behavior)
    window.location.hash = hash;
  });
} // End if
});
});
</script>
Atif Tariq
  • 2,351
  • 23
  • 33
2

I did this for both "/xxxxx#asdf" and "#asdf" href anchors

$("a[href*=#]").on('click', function(event){
    var href = $(this).attr("href");
    if ( /(#.*)/.test(href) ){
      var hash = href.match(/(#.*)/)[0];
      var path = href.match(/([^#]*)/)[0];

      if (window.location.pathname == path || path.length == 0){
        event.preventDefault();
        $('html,body').animate({scrollTop:$(this.hash).offset().top}, 1000);
        window.location.hash = hash;
      }
    }
});
AndreDurao
  • 4,687
  • 5
  • 36
  • 53
2

Here is the solution I implemented for multiple links and anchors, for a smooth scroll:

http://www.adriantomic.se/development/jquery-localscroll-tutorial/ if you have your navigation links set up in a navigation div and declared with this structure:

<a href = "#destinationA">

and your corresponding anchor tag destinations as so:

<a id = "destinationA">

Then just load this into the head of the document:

    <!-- Load jQuery -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>

<!-- Load ScrollTo -->
<script src="http://flesler-plugins.googlecode.com/files/jquery.scrollTo-1.4.2-min.js"></script>

<!-- Load LocalScroll -->
<script src="http://flesler-plugins.googlecode.com/files/jquery.localscroll-1.2.7-min.js"></script>

<script type = "text/javascript">
 $(document).ready(function()
    {
        // Scroll the whole document
        $('#menuBox').localScroll({
           target:'#content'
        });
    });
</script>

Thanks to @Adriantomic

collyg
  • 153
  • 1
  • 7
2

If you have a simple button on the page to scroll down to a div and want the back button to work by jumping to top, just add this code:

$(window).on('hashchange', function(event) {
    if (event.target.location.hash=="") {
        window.scrollTo(0,0);
    }
});

This could be extended to jump to different divs too, by reading the hash value, and scrolling like Joseph Silbers answer.

Niclas
  • 1,140
  • 1
  • 9
  • 19
2

Never forget that offset() function is giving your element's position to document. So when you need scroll your element relative to its parent you should use this;

    $('.a-parent-div').find('a').click(function(event){
        event.preventDefault();
        $('.scroll-div').animate({
     scrollTop: $( $.attr(this, 'href') ).position().top + $('.scroll-div').scrollTop()
     }, 500);       
  });

The key point is getting scrollTop of scroll-div and add it to scrollTop. If you won't do that position() function always gives you different position values.

Başar Söker
  • 367
  • 2
  • 11
2

You can use window.scroll() with behavior: smooth and top set to the anchor tag's offset top which ensures that the anchor tag will be at the top of the viewport.

document.querySelectorAll('a[href^="#"]').forEach(a => {
    a.addEventListener('click', function (e) {
        e.preventDefault();
        var href = this.getAttribute("href");
        var elem = document.querySelector(href)||document.querySelector("a[name="+href.substring(1, href.length)+"]");
        //gets Element with an id of the link's href 
        //or an anchor tag with a name attribute of the href of the link without the #
        window.scroll({
            top: elem.offsetTop, 
            left: 0, 
            behavior: 'smooth' 
        });
        //if you want to add the hash to window.location.hash
        //you will need to use setTimeout to prevent losing the smooth scrolling behavior
       //the following code will work for that purpose
       /*setTimeout(function(){
            window.location.hash = this.hash;
        }, 2000); */
    });
});

Demo:

a, a:visited{
  color: blue;
}

section{
  margin: 500px 0px; 
  text-align: center;
}
<a href="#section1">Section 1</a>
<br/>
<a href="#section2">Section 2</a>
<br/>
<a href="#section3">Section 3</a>
<br/>
<a href="#section4">Section 4</a>
<section id="section1">
<b style="font-size: 2em;">Section 1</b>
<p>Lorem ipsum dolor sit amet, et vis laudem utroque, iusto forensibus neglegentur eu duo. Eu pro fuisset salutandi philosophia, discere persecuti qui te. Eos ad quodsi dissentias, ei odio viris signiferumque mei. Putent iuvaret perpetua nec eu. Has no ornatus vivendum. Adhuc nonumes ex vim, in suas rebum graecis mei, usu ad causae recusabo. Idque vituperata vel ea.

Veri verterem pro ex. Ad error omnes est, id sit lorem legendos. Eos vidit ullum ne, tale tantas omittam est ut. Nobis maiorum efficiendi eu mei. Eos et debet placerat signiferumque. Per eu propriae electram.

Impetus percipit menandri te ius, mea ne stet posse fabellas. Aliquid corrumpit vel no, mei in diam praesent contentiones. Qui veniam suscipit probatus ex. No autem homero perfecto quo, eos choro facilis ut. Te quo cibo interesset. Vel verear praesent in, menandri deserunt ad his.

Labore admodum consetetur has et. Possit facilisi eu sed, lorem iriure eum id, pri ei consul necessitatibus. Est te iusto epicuri. Vis no graece putent mentitum, rebum facete offendit nec in. In duis vivendo sed, vel id enim voluptatibus. Velit sanctus ne mel, quem sumo suavitate mel cu, mea ea nullam feugiat.

Tincidunt suscipiantur no pro. Vel ut novum mucius molestie, ut tale ipsum intellegebat mei, mazim accumsan voluptaria ea nam. Posidonium theophrastus ut sea, stet viris hendrerit pro ex, sonet mentitum ne quo. Vim duis feugiat ex, nec eu probo doming persecuti. Velit zril nam in, est commodo splendide id. Et aperiri fuisset iracundia usu. Eu nec iusto audire repudiare.<p/>
<section>
<section id="section2">
<b style="font-size: 2em;">Section 2</b>
<p>Lorem ipsum dolor sit amet, et vis laudem utroque, iusto forensibus neglegentur eu duo. Eu pro fuisset salutandi philosophia, discere persecuti qui te. Eos ad quodsi dissentias, ei odio viris signiferumque mei. Putent iuvaret perpetua nec eu. Has no ornatus vivendum. Adhuc nonumes ex vim, in suas rebum graecis mei, usu ad causae recusabo. Idque vituperata vel ea.

Veri verterem pro ex. Ad error omnes est, id sit lorem legendos. Eos vidit ullum ne, tale tantas omittam est ut. Nobis maiorum efficiendi eu mei. Eos et debet placerat signiferumque. Per eu propriae electram.

Impetus percipit menandri te ius, mea ne stet posse fabellas. Aliquid corrumpit vel no, mei in diam praesent contentiones. Qui veniam suscipit probatus ex. No autem homero perfecto quo, eos choro facilis ut. Te quo cibo interesset. Vel verear praesent in, menandri deserunt ad his.

Labore admodum consetetur has et. Possit facilisi eu sed, lorem iriure eum id, pri ei consul necessitatibus. Est te iusto epicuri. Vis no graece putent mentitum, rebum facete offendit nec in. In duis vivendo sed, vel id enim voluptatibus. Velit sanctus ne mel, quem sumo suavitate mel cu, mea ea nullam feugiat.

Tincidunt suscipiantur no pro. Vel ut novum mucius molestie, ut tale ipsum intellegebat mei, mazim accumsan voluptaria ea nam. Posidonium theophrastus ut sea, stet viris hendrerit pro ex, sonet mentitum ne quo. Vim duis feugiat ex, nec eu probo doming persecuti. Velit zril nam in, est commodo splendide id. Et aperiri fuisset iracundia usu. Eu nec iusto audire repudiare.</p>
<section>
<section id="section3">
<b style="font-size: 2em;">Section 3</b>
<p>
Lorem ipsum dolor sit amet, et vis laudem utroque, iusto forensibus neglegentur eu duo. Eu pro fuisset salutandi philosophia, discere persecuti qui te. Eos ad quodsi dissentias, ei odio viris signiferumque mei. Putent iuvaret perpetua nec eu. Has no ornatus vivendum. Adhuc nonumes ex vim, in suas rebum graecis mei, usu ad causae recusabo. Idque vituperata vel ea.

Veri verterem pro ex. Ad error omnes est, id sit lorem legendos. Eos vidit ullum ne, tale tantas omittam est ut. Nobis maiorum efficiendi eu mei. Eos et debet placerat signiferumque. Per eu propriae electram.

Impetus percipit menandri te ius, mea ne stet posse fabellas. Aliquid corrumpit vel no, mei in diam praesent contentiones. Qui veniam suscipit probatus ex. No autem homero perfecto quo, eos choro facilis ut. Te quo cibo interesset. Vel verear praesent in, menandri deserunt ad his.

Labore admodum consetetur has et. Possit facilisi eu sed, lorem iriure eum id, pri ei consul necessitatibus. Est te iusto epicuri. Vis no graece putent mentitum, rebum facete offendit nec in. In duis vivendo sed, vel id enim voluptatibus. Velit sanctus ne mel, quem sumo suavitate mel cu, mea ea nullam feugiat.

Tincidunt suscipiantur no pro. Vel ut novum mucius molestie, ut tale ipsum intellegebat mei, mazim accumsan voluptaria ea nam. Posidonium theophrastus ut sea, stet viris hendrerit pro ex, sonet mentitum ne quo. Vim duis feugiat ex, nec eu probo doming persecuti. Velit zril nam in, est commodo splendide id. Et aperiri fuisset iracundia usu. Eu nec iusto audire repudiare.</p>
<section>
<a style="margin: 500px 0px; color: initial;" name="section4">
<b style="font-size: 2em;">Section 4 <i>(this is an anchor tag, not a section)</i></b>
</a>
<p>
Lorem ipsum dolor sit amet, et vis laudem utroque, iusto forensibus neglegentur eu duo. Eu pro fuisset salutandi philosophia, discere persecuti qui te. Eos ad quodsi dissentias, ei odio viris signiferumque mei. Putent iuvaret perpetua nec eu. Has no ornatus vivendum. Adhuc nonumes ex vim, in suas rebum graecis mei, usu ad causae recusabo. Idque vituperata vel ea.

Veri verterem pro ex. Ad error omnes est, id sit lorem legendos. Eos vidit ullum ne, tale tantas omittam est ut. Nobis maiorum efficiendi eu mei. Eos et debet placerat signiferumque. Per eu propriae electram.

Impetus percipit menandri te ius, mea ne stet posse fabellas. Aliquid corrumpit vel no, mei in diam praesent contentiones. Qui veniam suscipit probatus ex. No autem homero perfecto quo, eos choro facilis ut. Te quo cibo interesset. Vel verear praesent in, menandri deserunt ad his.

Labore admodum consetetur has et. Possit facilisi eu sed, lorem iriure eum id, pri ei consul necessitatibus. Est te iusto epicuri. Vis no graece putent mentitum, rebum facete offendit nec in. In duis vivendo sed, vel id enim voluptatibus. Velit sanctus ne mel, quem sumo suavitate mel cu, mea ea nullam feugiat.

Tincidunt suscipiantur no pro. Vel ut novum mucius molestie, ut tale ipsum intellegebat mei, mazim accumsan voluptaria ea nam. Posidonium theophrastus ut sea, stet viris hendrerit pro ex, sonet mentitum ne quo. Vim duis feugiat ex, nec eu probo doming persecuti. Velit zril nam in, est commodo splendide id. Et aperiri fuisset iracundia usu. Eu nec iusto audire repudiare.</p>
<script>
document.querySelectorAll('a[href^="#"]').forEach(a => {
        a.addEventListener('click', function (e) {
            e.preventDefault();
            var href = this.getAttribute("href");
            var elem = document.querySelector(href)||document.querySelector("a[name="+href.substring(1, href.length)+"]");
            window.scroll({
                top: elem.offsetTop, 
                left: 0, 
                behavior: 'smooth' 
            });
        });
    });
</script>

You can just set the CSS property scroll-behavior to smooth (which most modern browsers support) which obviates the need for Javascript.

html, body{
  scroll-behavior: smooth;
}
a, a:visited{
  color: blue;
}

section{
  margin: 500px 0px; 
  text-align: center;
}
<a href="#section1">Section 1</a>
<br/>
<a href="#section2">Section 2</a>
<br/>
<a href="#section3">Section 3</a>
<br/>
<a href="#section4">Section 4</a>
<section id="section1">
<b style="font-size: 2em;">Section 1</b>
<p>Lorem ipsum dolor sit amet, et vis laudem utroque, iusto forensibus neglegentur eu duo. Eu pro fuisset salutandi philosophia, discere persecuti qui te. Eos ad quodsi dissentias, ei odio viris signiferumque mei. Putent iuvaret perpetua nec eu. Has no ornatus vivendum. Adhuc nonumes ex vim, in suas rebum graecis mei, usu ad causae recusabo. Idque vituperata vel ea.

Veri verterem pro ex. Ad error omnes est, id sit lorem legendos. Eos vidit ullum ne, tale tantas omittam est ut. Nobis maiorum efficiendi eu mei. Eos et debet placerat signiferumque. Per eu propriae electram.

Impetus percipit menandri te ius, mea ne stet posse fabellas. Aliquid corrumpit vel no, mei in diam praesent contentiones. Qui veniam suscipit probatus ex. No autem homero perfecto quo, eos choro facilis ut. Te quo cibo interesset. Vel verear praesent in, menandri deserunt ad his.

Labore admodum consetetur has et. Possit facilisi eu sed, lorem iriure eum id, pri ei consul necessitatibus. Est te iusto epicuri. Vis no graece putent mentitum, rebum facete offendit nec in. In duis vivendo sed, vel id enim voluptatibus. Velit sanctus ne mel, quem sumo suavitate mel cu, mea ea nullam feugiat.

Tincidunt suscipiantur no pro. Vel ut novum mucius molestie, ut tale ipsum intellegebat mei, mazim accumsan voluptaria ea nam. Posidonium theophrastus ut sea, stet viris hendrerit pro ex, sonet mentitum ne quo. Vim duis feugiat ex, nec eu probo doming persecuti. Velit zril nam in, est commodo splendide id. Et aperiri fuisset iracundia usu. Eu nec iusto audire repudiare.<p/>
<section>
<section id="section2">
<b style="font-size: 2em;">Section 2</b>
<p>Lorem ipsum dolor sit amet, et vis laudem utroque, iusto forensibus neglegentur eu duo. Eu pro fuisset salutandi philosophia, discere persecuti qui te. Eos ad quodsi dissentias, ei odio viris signiferumque mei. Putent iuvaret perpetua nec eu. Has no ornatus vivendum. Adhuc nonumes ex vim, in suas rebum graecis mei, usu ad causae recusabo. Idque vituperata vel ea.

Veri verterem pro ex. Ad error omnes est, id sit lorem legendos. Eos vidit ullum ne, tale tantas omittam est ut. Nobis maiorum efficiendi eu mei. Eos et debet placerat signiferumque. Per eu propriae electram.

Impetus percipit menandri te ius, mea ne stet posse fabellas. Aliquid corrumpit vel no, mei in diam praesent contentiones. Qui veniam suscipit probatus ex. No autem homero perfecto quo, eos choro facilis ut. Te quo cibo interesset. Vel verear praesent in, menandri deserunt ad his.

Labore admodum consetetur has et. Possit facilisi eu sed, lorem iriure eum id, pri ei consul necessitatibus. Est te iusto epicuri. Vis no graece putent mentitum, rebum facete offendit nec in. In duis vivendo sed, vel id enim voluptatibus. Velit sanctus ne mel, quem sumo suavitate mel cu, mea ea nullam feugiat.

Tincidunt suscipiantur no pro. Vel ut novum mucius molestie, ut tale ipsum intellegebat mei, mazim accumsan voluptaria ea nam. Posidonium theophrastus ut sea, stet viris hendrerit pro ex, sonet mentitum ne quo. Vim duis feugiat ex, nec eu probo doming persecuti. Velit zril nam in, est commodo splendide id. Et aperiri fuisset iracundia usu. Eu nec iusto audire repudiare.</p>
<section>
<section id="section3">
<b style="font-size: 2em;">Section 3</b>
<p>
Lorem ipsum dolor sit amet, et vis laudem utroque, iusto forensibus neglegentur eu duo. Eu pro fuisset salutandi philosophia, discere persecuti qui te. Eos ad quodsi dissentias, ei odio viris signiferumque mei. Putent iuvaret perpetua nec eu. Has no ornatus vivendum. Adhuc nonumes ex vim, in suas rebum graecis mei, usu ad causae recusabo. Idque vituperata vel ea.

Veri verterem pro ex. Ad error omnes est, id sit lorem legendos. Eos vidit ullum ne, tale tantas omittam est ut. Nobis maiorum efficiendi eu mei. Eos et debet placerat signiferumque. Per eu propriae electram.

Impetus percipit menandri te ius, mea ne stet posse fabellas. Aliquid corrumpit vel no, mei in diam praesent contentiones. Qui veniam suscipit probatus ex. No autem homero perfecto quo, eos choro facilis ut. Te quo cibo interesset. Vel verear praesent in, menandri deserunt ad his.

Labore admodum consetetur has et. Possit facilisi eu sed, lorem iriure eum id, pri ei consul necessitatibus. Est te iusto epicuri. Vis no graece putent mentitum, rebum facete offendit nec in. In duis vivendo sed, vel id enim voluptatibus. Velit sanctus ne mel, quem sumo suavitate mel cu, mea ea nullam feugiat.

Tincidunt suscipiantur no pro. Vel ut novum mucius molestie, ut tale ipsum intellegebat mei, mazim accumsan voluptaria ea nam. Posidonium theophrastus ut sea, stet viris hendrerit pro ex, sonet mentitum ne quo. Vim duis feugiat ex, nec eu probo doming persecuti. Velit zril nam in, est commodo splendide id. Et aperiri fuisset iracundia usu. Eu nec iusto audire repudiare.</p>
<section>
<a style="margin: 500px 0px; color: initial;" name="section4">
<b style="font-size: 2em;">Section 4 <i>(this is an anchor tag, not a section)</i></b>
</a>
<p>
Lorem ipsum dolor sit amet, et vis laudem utroque, iusto forensibus neglegentur eu duo. Eu pro fuisset salutandi philosophia, discere persecuti qui te. Eos ad quodsi dissentias, ei odio viris signiferumque mei. Putent iuvaret perpetua nec eu. Has no ornatus vivendum. Adhuc nonumes ex vim, in suas rebum graecis mei, usu ad causae recusabo. Idque vituperata vel ea.

Veri verterem pro ex. Ad error omnes est, id sit lorem legendos. Eos vidit ullum ne, tale tantas omittam est ut. Nobis maiorum efficiendi eu mei. Eos et debet placerat signiferumque. Per eu propriae electram.

Impetus percipit menandri te ius, mea ne stet posse fabellas. Aliquid corrumpit vel no, mei in diam praesent contentiones. Qui veniam suscipit probatus ex. No autem homero perfecto quo, eos choro facilis ut. Te quo cibo interesset. Vel verear praesent in, menandri deserunt ad his.

Labore admodum consetetur has et. Possit facilisi eu sed, lorem iriure eum id, pri ei consul necessitatibus. Est te iusto epicuri. Vis no graece putent mentitum, rebum facete offendit nec in. In duis vivendo sed, vel id enim voluptatibus. Velit sanctus ne mel, quem sumo suavitate mel cu, mea ea nullam feugiat.

Tincidunt suscipiantur no pro. Vel ut novum mucius molestie, ut tale ipsum intellegebat mei, mazim accumsan voluptaria ea nam. Posidonium theophrastus ut sea, stet viris hendrerit pro ex, sonet mentitum ne quo. Vim duis feugiat ex, nec eu probo doming persecuti. Velit zril nam in, est commodo splendide id. Et aperiri fuisset iracundia usu. Eu nec iusto audire repudiare.</p>
iota
  • 34,586
  • 7
  • 32
  • 51
1

thanks for sharing, Joseph Silber. Here your 2018 solution as ES6 with a minor change to keep the standard behavior (scroll to top):

document.querySelectorAll("a[href^=\"#\"]").forEach((anchor) => {
  anchor.addEventListener("click", function (ev) {
    ev.preventDefault();

    const targetElement = document.querySelector(this.getAttribute("href"));
    targetElement.scrollIntoView({
      block: "start",
      alignToTop: true,
      behavior: "smooth"
    });
  });
});
Motine
  • 1,206
  • 12
  • 14
1

Requires jquery and animates to anchor tag with the specified name instead of id, while adding the hash to browser url. Also fixes an error in most answers with jquery where the # sign is not prefixed with an escaping backslash. The back button, unfortunately, does not navigate back properly to previous hash links...

$('a[href*=\\#]').click(function (event)
{
    let hashValue = $(this).attr('href');
    let name = hashValue.substring(1);
    let target = $('[name="' + name + '"]');
    $('html, body').animate({ scrollTop: target.offset().top }, 500);
    event.preventDefault();
    history.pushState(null, null, hashValue);
});
jjxtra
  • 18,300
  • 12
  • 85
  • 128
1

Well, the solution depends on the type of problem, I use the javascript animate method for the button click. and I use codes from bellow links for the navbar

https://css-tricks.com/snippets/jquery/smooth-scrolling/

$(document).ready(function () {
  $(".js--scroll-to-plans").click(function () {
    $("body,html").animate(
      {
        scrollTop: $(".js--section-plans").offset().top,
      },
      1000
    );
    return false;
  });

  $(".js--scroll-to-start").click(function () {
    $("body,html").animate(
      {
        scrollTop: $(".js--section-features").offset().top,
      },
      1000
    );
    return false;
  });

  $('a[href*="#"]')
  // Remove links that don't actually link to anything
  .not('[href="#"]')
  .not('[href="#0"]')
  .click(function(event) {
    // On-page links
    if (
      location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') 
      && 
      location.hostname == this.hostname
    ) {
      // Figure out element to scroll to
      var target = $(this.hash);
      target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
      // Does a scroll target exist?
      if (target.length) {
        // Only prevent default if animation is actually gonna happen
        event.preventDefault();
        $('html, body').animate({
          scrollTop: target.offset().top
        }, 1000, function() {
          // Callback after animation
          // Must change focus!
          var $target = $(target);
          $target.focus();
          if ($target.is(":focus")) { // Checking if the target was focused
            return false;
          } else {
            $target.attr('tabindex','-1'); // Adding tabindex for elements not focusable
            $target.focus(); // Set focus again
          };
        });
      }
    }
  });
});
Lord
  • 2,830
  • 10
  • 17