Jquery animate offset doesnt scroll to right point

Hi all

I have a Bootstrap scrollspy nav. If you view this page http://www.exeter.ac.uk/codebox/dan/exeterV2/final-code/course-page.php and scroll halfway down to the grey menu which has “Entry requirements, Fees and funding, Course content etc…”.

Becuase Ive changed the height of the scrollspy menu, when you click on a link it doesnt quite take you to the relevant title. For example, click on Course content; it takes you about 150px past where it should do so the “Course content” title is hidden behind the scrollspy menu.

Ive tried a few things including putting a minus figure after the line:

scrollTop: $(hash).offset().top

But this makes the animation jump.

Can anyone see what the issue is? (code starts on line 1302 in the code).

Many thanks

Hi @bolton, it does get you to the correct position but the content is partially hidden behind the fixed menu; try subtracting that menu’s height from the target scroll top position.

Thanks @m3g4p0p

Im now subtracting the scrollspy’s height, which has fixed that issue.
Unfortunately its raised 2 more issues:

  1. Notice the url changes when you click on the sub nav items. (this is so that someone can link directly to the ‘Fees and funding’ section etc… and the page will automatically scroll to that portion of the page). It was appending “#section1 etc…” to the url. It now appends “#NaN” becuase Ive changed the line 878:

window.location.hash = hash - scrollspyheight

  1. Notice the background color of the active section’s button ‘sticks’ when you click between sections. This wasnt happening before.

Many thanks for your assistance with this. :slight_smile:

Well hash is a string, but the - operator only works for numbers; and the failed attempt to convert "#section1" etc. to a number results in NaN (not a number). But encoding a scroll offset in a fragment identifier won’t work anyway, you’ll have to adjust the scroll position with JS once the page has loaded.

That’s probably because of the messed up fragment identifiers then… it should work again after you fixed this.

Thanks @m3g4p0p . Ive got teh has working again, but still cant get the scroll offset working. The page wont be reloading when the user clicks on those buttons (Entry requirements, Fees and funding, Course content etc…) so not sure “… you’ll have to adjust the scroll position with JS once the page has loaded.” will work?

1 Like

Excuse me for jumping in but looking at the end of your scroll routine you have window.location.hash = hash;
e.g.


	$('html, body').animate({
       scrollTop: ($(hash).offset().top - scrollspyheight)
     }, 1000, function(){
       window.location.hash = hash;
     });

I see three issues altogether

  1. No matter where you have nicely scrolled the page it will then jump to the window location and if that’s not the same as the position it already occupies then there will be a jump

  2. I believe the scrollspy has an offset method that will change the active tab to account for the fixed header but this has nothing to do with the scrolling as such.
    $(“body”).scrollspy({offset: 143});

  3. I believe you calculate the height of the tab nav before you have scrolled and becomes fixed and therefore its height doesn’t match at the end of the scroll.

scrollTop: ($(hash).offset().top - scrollspyheight)

I can’t really help with the JS but it might help @m3g4p0p offer you a solution :slight_smile:

In testing the fixed menu works perfectly once it is fixed to the top and using the offset mentioned but removing the location.hash.

e.g.

// add relevant class to body for scrollspy
$('body').attr("data-spy", "scroll");
$('body').attr("data-target", "#scrollspy");
$("body").scrollspy({offset: 143});

// animate scrollspy
$("#scrollspy a[href^='#']").on('click', function(e) {
   e.preventDefault();
   var hash = this.hash;
   var scrollspyheight = $("#scrollspy").height();
   
   
	$('html, body').animate({
       scrollTop: ($(hash).offset().top - scrollspyheight)
     }, 1000, function(){
       //window.location.hash = hash;
     });

});
// dynamically affix the scrollspy at its offset
$("#scrollspy").attr("data-offset-top", Math.round($( "#scrollspy" ).offset().top - 100));

I guess you would need to find another method of changing the address bar rather than location.hash. (I’m also assuming that you would need to catch external links to the fragment identifier and manage them also.)

Hope some of the above is of some use but I doubt it :slight_smile:

1 Like

Ah yes good catch… this could be done by modifying the history then. Here’s a quick vanilla JS example:

document.querySelectorAll('[href^="#"').forEach(link => {
  link.addEventListener('click', event => {
    const scrollTarget = document.querySelector(event.target.hash)

    event.preventDefault()
    window.history.pushState({}, document.title, event.target.href)
    scrollTarget.scrollIntoView({ behavior: 'smooth' })
  })
})

This will create a new history entry and update the location in the browser address bar (just like the default link behaviour), but, unlike setting the location directly, not cause any scrolling or page reloads.

PS: Or with your code, accounting for the scroll offset:

$("#scrollspy a[href^='#']").on('click', function (e) {
  e.preventDefault()
  window.history.pushState({}, document.title, this.href)

  $('html, body').animate({
    scrollTop: $(this.hash).offset().top - $('#scrollspy').height()
  }, 1000)
})
1 Like

Thanks so much guys! Its almost there!

If you follow a direct link to a section such as http://www.exeter.ac.uk/codebox/dan/exeterV2/final-code/course-page.php#section4 theres no offset. The title is hidden behind the menu.

Also, sometimes clicking between the links the offset goes askew again. For example, click between the menu links. If you click on Entry requirements and then one of the other links youll see the offset doesnt take effect.

Thansk so much for your assistance with this! :slight_smile:

Yes as I said earlier, you’ll have to adjust the scroll position manually then; for instance:

window.scrollBy({ top: -$('#scrollspy').height() })

The carousel at the beginning is changing its height, making the whole content jump in certain intervals. The browser would compensate for this (or more probably simply not repaint) when the carousel is not in the viewport, but it still messes up the calculation of the target scroll position. I’d suggest to give that carousel a fixed height anyway.

1 Like

OK great - the page now scrolls to the relevant section if a section is linked to directly.

Ive also removed that carousel with the varying heights, but the scrollspy still goes out of sync. This happens if you click on the buttons AND THEN click on the Entry Requirements AND THEN click on one of the other buttons. Very odd! Its almost as if if you click on the first button it messes up the offset.

After you scroll up from below the first section, the nav bar just turns non-sticky again so there’s no need for an offset… other than that I can’t reproduce the issue. Can capture your screen and post it here as a gif or something?

Can you try this sequence of events: Scroll down the page until that nav becomes affixed; Click on Course content; then click on Learning and teaching. (All works fine so far!); Now click on Entry requirements. (Still works fine); Now click Course content. Youll see the page scrolls halfway down the Course content section!

Are you able to replicate this?

Ah yes you’re right – in fact, it happens whenever you click a link from the non-sticky nav as it has a different height then. To fix this, you might simply add the affix class manually before calculating the offset.