Scrollspy Using Full URL
Asked Answered
C

4

11

I am trying to get scrollspy to work when using an absolute url rather than an # anchor link.

For example, I can easily run scrollspy by using only <a href="#section1"></a>

I would like to be able to run scrollspy by using <a href="http://myurl.com/#section1"></a>

The reason for doing this, is the main navigation on the homepage is scorllspy, but the blog is not part of the homepage. When visiting the blog and then clicking a link to somthing on the homepage, you get routed to http://myurl.com/blog/#section1 rather than the homepage ID.

I found this solution ( use url and hash with bootstrap scrollspy ) , but could not get it to fully function. After some tweaking and a slew of regex combinations I could get it to add the active class to the first menu item, but it would not refresh.

Any thoughts on getting this to work? Is there an alternative js library I should use?

Solution thanks to Troy

jQuery(function($){
  // local url of page (minus any hash, but including any potential query string)
  var url = location.href.replace(/#.*/,'');

  // Find all anchors
  $('.nav-main').find('a[href]').each(function(i,a){
    var $a = $(a);
    var href = $a.attr('href');

    // check is anchor href starts with page's URI
    if (href.indexOf(url+'#') == 0) {
      // remove URI from href
      href = href.replace(url,'');
      // update anchors HREF with new one
      $a.attr('href',href);
    }

    // Now refresh scrollspy
    $('[data-spy="scroll"]').each(function (i,spy) {
      var $spy = $(this).scrollspy('refresh')
    })
  });

  $('body').scrollspy({ target: '.nav-main', offset: 155 })

});

I added $('body').scrollspy({ target: '.nav-main', offset: 155 }) in order to get the data offset to re-apply after refreshing scrollspy. After some trial and error, this is the only solution I was able to find.

Circumnutate answered 14/1, 2014 at 6:38 Comment(0)
C
5

The way scrollspy is coded, it looks at anchors that have href's that start with a #.

There is one way you could do this. Not pretty but may work.

Once the page loads, in your jQuery ready function, search the document for all anchors (inside of your target container, or body), and check to see if the href starts with your hostname, followed by a/#, and if it matches change the anchor's href to just be the portion after the #

Here is some rough code (not tested) that you could try (say your scrollspy target is a div with id of #navbar:

jQuery(function($){
  // local url of page (minus any hash, but including any potential query string)
  var url = location.href.replace(/#.*/,'');

  // Find all anchors
  $('#navbar').find('a[href]').each(function(i,a){
    var $a = $(a);
    var href = $a.attr('href');

    // check is anchor href starts with page's URI
    if (href.indexOf(url+'#') == 0) {
      // remove URI from href
      href = href.replace(url,'');
      // update anchors HREF with new one
      $a.attr('href',href);
    }

    // Now refresh scrollspy
    $('[data-spy="scroll"]').each(function (i,spy) {
       $(spy).scrollspy('refresh');
    });
  });

});

Now make sure you run this after you load the bootstrap.min.js file.

If you are initializing your ScrollSpy via javascript, rather than via data-* attributes, then replace the three lines of code where scrollspy is refreshed wit the code to initialise your scrollspy target & settings.

Christly answered 15/1, 2014 at 0:57 Comment(3)
Now I just noticed that you mentioned that your blog is not part of the homepage. Is it in an iframe or frame? If it is, scrollspy will not work across documents (frames are in separate document elements)Christly
Its part of the same domain. It is just when using anchor IDs rather than full urls, the navigation becomes useless on all but the homepage. I tried the above code but was unable to get it to work. I changed the id of the nav, to the id I am using to no avail. You can see what I am working with here - bic.bldsvr.comCircumnutate
I figured it out. I am using the roots starter theme and it strips relative URLS which break the whole system. Your code is needed and does work. Note to roots theme users disable relative-urls.phpCircumnutate
W
38

Or you can add data-target attribute to your link element: data-target="#link"

<a href="http://www.site.ru/#link1" data-target="#link1">Link1</a>
<a href="http://www.site.ru/#link2" data-target="#link2">Link2</a>
Weirdie answered 9/2, 2014 at 20:52 Comment(6)
This is true but in my case I am using WordPress and doing so would require a custom walker. In the interest of saving time, I chose the route of less resistance.Circumnutate
This was easy to implement in WordPress since I already had a custom walker, thanks @Weirdie :)Hilel
Great solution and no need to do any hacks. Perfect.Kline
I am using it with Flask but so my "href=/link1/#link1" with data-target="#link1" is not working.Gazelle
Not working in my context (scrollspy initialized by JavaScript on body + .NET 6 Blazor).Maramarabel
In my context (blazor server) I don't need data-target, scrollspy works well with absolute URL.Maramarabel
C
5

The way scrollspy is coded, it looks at anchors that have href's that start with a #.

There is one way you could do this. Not pretty but may work.

Once the page loads, in your jQuery ready function, search the document for all anchors (inside of your target container, or body), and check to see if the href starts with your hostname, followed by a/#, and if it matches change the anchor's href to just be the portion after the #

Here is some rough code (not tested) that you could try (say your scrollspy target is a div with id of #navbar:

jQuery(function($){
  // local url of page (minus any hash, but including any potential query string)
  var url = location.href.replace(/#.*/,'');

  // Find all anchors
  $('#navbar').find('a[href]').each(function(i,a){
    var $a = $(a);
    var href = $a.attr('href');

    // check is anchor href starts with page's URI
    if (href.indexOf(url+'#') == 0) {
      // remove URI from href
      href = href.replace(url,'');
      // update anchors HREF with new one
      $a.attr('href',href);
    }

    // Now refresh scrollspy
    $('[data-spy="scroll"]').each(function (i,spy) {
       $(spy).scrollspy('refresh');
    });
  });

});

Now make sure you run this after you load the bootstrap.min.js file.

If you are initializing your ScrollSpy via javascript, rather than via data-* attributes, then replace the three lines of code where scrollspy is refreshed wit the code to initialise your scrollspy target & settings.

Christly answered 15/1, 2014 at 0:57 Comment(3)
Now I just noticed that you mentioned that your blog is not part of the homepage. Is it in an iframe or frame? If it is, scrollspy will not work across documents (frames are in separate document elements)Christly
Its part of the same domain. It is just when using anchor IDs rather than full urls, the navigation becomes useless on all but the homepage. I tried the above code but was unable to get it to work. I changed the id of the nav, to the id I am using to no avail. You can see what I am working with here - bic.bldsvr.comCircumnutate
I figured it out. I am using the roots starter theme and it strips relative URLS which break the whole system. Your code is needed and does work. Note to roots theme users disable relative-urls.phpCircumnutate
V
0

I hope this helps anyone who runs into this same situation as I did.

View it live: Live Sample (Handyman Website built on WordPress, roots.io, and Bootstrap 3)

You can see below that I conditionally only perform this when not on the homepage. This leaves the scroll spy behavior untouched on the homepage and alters the links to link to their respective sections on the homepage when clicked from non-homepage pages.

This is what I came up with (fairly well tested)

You would simply need to replace the jQuery/css selectors as needed.

Here is the full snippet which is using scroll spy, the above solution, and a bit of smooth section scrolling action

/**
 * Scroll Spy via Bootstrap
 */
$('body').scrollspy({target: "#nav_wrapper", offset:50});

/**
 * Scroll Spy meet WordPress menu.
 */
// Only fire when not on the homepage
if (window.location.pathname !== "/") {
    // Selector for top nav a elements
    $('#top_main_nav a').each( function() {
        if (this.hash.indexOf('#') === 0) {
            // Alert this to an absolute link (pointing to the correct section on the hompage)
            this.href = "/" + this.hash;
        }
    });
}

/**
 * Initiate Awesome Smooth Scrolling based on a.href
 */
$('a.smooth-scroll, #top_main_nav a').click( function() {
    target = $(this).attr('href');
    offset = $(target).offset();
    $('body,html').animate({ scrollTop : offset.top }, 700);
    event.preventDefault();
});

View it Live Here

Vannesavanness answered 17/4, 2014 at 4:22 Comment(0)
P
0

simply add this onclick function to the anchor tag and you should done!

<a href="#" onclick="window.location.href='https://www.google.com/'" class="nav-link">Google</a>
Peroneus answered 9/3, 2020 at 6:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.