kswedberg/jquery-smooth-scroll

Load different page and then smooth scroll within different page

karland opened this issue ยท 11 comments

Hi, thanks for jquery-smooth-scroll. I think it is excellent.

I would like the smooth scrolling to take place on a different page. So my element on different-page.html looks like

<a href="/index.html#to-chapter1"> To Chapter 1</a>

I thought I can use the beforeScroll-function.

// smooth-scroll
$('a[href*=#]:not([href=#])').click(function() {
    $.smoothScroll({
        beforeScroll: function () {location.replace("/index.html");},
        offset: -120,
        scrollTarget: this.hash
    });
    return false;
});

However, smooth-scroll does not wait for the beforeScroll-function to be completed, i.e. the page to be loaded. Is there a way to make this happen? Thanks

Hi, I did something like this and here are a couple of tips I can pass on.

Firstly, to prevent the browser from jumping straight to the target when the page loads you can add a suffix to all the scroll target ids, this stops them from immediately matching up with the hash from the address bar. So the target of page.html#target gets an id of target_hash rather than just target. Later some JS adds the suffix to the scrollTarget before triggering smoothScroll.

Secondly, I read that for browser compatibility reasons it's worth putting in a short delay before testing the window.location for hash. And it's also worth putting a short delay before executing the smoothScroll function. I don't remember where I read this but the idea is to ensures the page is sufficiently loaded before executing the smoothScroll.

Putting that all together you end up with some code like this. It works for me on a number of sites.

// Requires SmoothScroll and jQuery
$(document).ready(function() {

    /* Take over default page anchor functionality */

    // Store hash location
    var hashLocation = false;
    if (location.hash) {
        hashLocation = window.location.hash;
        setTimeout(function() {
            hashLocation = window.location.hash;
        }, 1); // Execute at two moments for browser compatibility reasons
    }

    // If we have a hash location do stuff
    if (hashLocation) {

        // delay .1s for browser compatibility reasons
        setTimeout(function() {

            // Check hashLocation suffix
            if( hashLocation.indexOf('_hash') < 0 ) {
                hashLocation = hashLocation + "_hash";
            };

            // Scroll to target
            $.smoothScroll({
                scrollTarget: hashLocation
            });

        }, 100);

    };
});

@LL782 thanks a lot. It helped me to find the solution below together with two other sources: 1 and 2.

My final solution looks like that (tested under IE11, Firefox, Chrome, Opera, Safari):

$(document).ready(function() {

    var scrollnow = function(e) {
        // if scrollnow()-function was triggered by an event
        if (e) {
            e.preventDefault();
            var target = this.hash;
        }
        // else it was called when page with a #hash was loaded
        else {
            var target = location.hash;
        }

        // same page scroll
        $.smoothScroll({
            offset: -120,
            scrollTarget: target
        });
    };

    // if page has a #hash
    if (location.hash) {
        $('html, body').scrollTop(0).show();
        // smooth-scroll to hash
        scrollnow();
    }

    // for each <a>-element that contains a "/" and a "#"
    $('a[href*="/"][href*=#]').each(function(){
        // if the pathname of the href references the same page
        if (this.pathname.replace(/^\//,'') == location.pathname.replace(/^\//,'') && this.hostname == location.hostname) {
            // only keep the hash, i.e. do not keep the pathname
            $(this).attr("href", this.hash);
        }
    });

    // select all href-elements that start with #
    // including the ones that were stripped by their pathname just above
    $('a[href^=#]:not([href=#])').click(scrollnow);

});

@karland : you can change the .each() loop to a .filter() function and call .click() on the filtered links:

// for each <a>-element that contains a "/" and a "#"
   $('a[href*="/"][href*=#]').filter(function(){
       // if the pathname of the href references the same page
       return this.hash && 
              this.pathname.replace(/^\//,'') == location.pathname.replace(/^\//,'') && 
              this.hostname == location.hostname;

   }).click(scrollnow);

I should have mentioned that a link like <a href="#"> (as opposed to <a href="#foo">) does not have a "hash," so this.hash would equal "".

@kswedberg Thanks a lot. I agree, your code is more elegant. However, I was not very clear of what I wanted to achieve: smooth-scroll for same page AND different page links. The only solution I was able to come up with is the following:

  1. For "same page links", that contain a path, I have to remove the path first and then bind scrollnow (wrapping smooth-scroll) to all "same page links".
  2. For all "different page links" I have to leave them as they are and smooth-scroll to the #hash tag, once a page is loaded.

The "problem" with my solution is that I am touching some <a>-elements twice, which I do not like so much, even if it works ...

@karland - thanks for the snippet. Helped me solve an issue.

Is the above code is working in 2020? I just copy and paste the karland answer but I am getting error in console.

Error: Syntax error, unrecognized expression: a[href*="/"][href*=#]

79851868-862afb00-83e3-11ea-82e2-5415e8ee06f6

@Naren-hybreeder , try putting quotes around the hash:

$('a[href*="/"][href*="#"]')
//โ€ฆ

@kswedberg Thanks for the reply, I tried, My above issue got resolved but when I click on anchor tag then I am getting error in the console.

$.smoothScroll is not a function

Screenshot 2020-04-21 at 10 46 20 PM

I tried the below code which is working for me. I tried below code from one page to anoter page and it's working perfeclty but this code is not working on the same page.

$(document).ready(function() {
       $('html, body').hide();
       if (window.location.hash) {
           setTimeout(function() {
               $('html, body').scrollTop(0).show();
               $('html, body').animate({
                   scrollTop: $(window.location.hash).offset().top-100
                   }, 1000)
           }, 0);
       }
       else {
           $('html, body').show();
       }
   });

@Naren-hybreeder - maybe try noConflict solution? I usually experience this issue if I'm trying to minimize, defer, or Async JS files.

@Naren-hybreeder , it's hard to troubleshoot without seeing what you're doing, but @dpanfili could be right. You might have more than one instance of jQuery loading on the page. Or, you're calling $.smoothScroll before the smooth scroll plugin has been loaded and parsed.