Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scrollspy: highlights wrong navigation item when a page is reloaded. #32496

Closed
DeboBuiz opened this issue Dec 15, 2020 · 34 comments · Fixed by #33603
Closed

Scrollspy: highlights wrong navigation item when a page is reloaded. #32496

DeboBuiz opened this issue Dec 15, 2020 · 34 comments · Fixed by #33603
Labels

Comments

@DeboBuiz
Copy link

DeboBuiz commented Dec 15, 2020

  • Operating system and version : Windows 10
  • Browser and version: Chrome latest

https://codepen.io/debobuiz/pen/ZEpempL

Issue: Please refer the screenshot. I have scrolled the page to Item 1. But in the navbar, "Item 3" is highlighted.

image

How to reproduce the issue:
When the page is opened initially, things will be fine. Scroll the page to item 3 or item 4. Then right-click and "reload frame" in the codepen.io preview section. Now, when you scroll the page through various items, the correct item will not be highlighted in the navbar.

@Neelam-Nishad
Copy link

I refreshed 2-3 times and its working fine

@DeboBuiz
Copy link
Author

DeboBuiz commented Dec 15, 2020 via email

@DeboBuiz
Copy link
Author

You may copy the code in a local html file too and recreate the issue by scrolling to a different section and reloading the page.

@Neelam-Nishad
Copy link

yes I understand the problem now

@Neelam-Nishad
Copy link

You may copy the code in a local html file too and recreate the issue by scrolling to a different section and reloading the page.

I copied the code in my local, and its really working fine there , even after reloads. may be the problem is with codepen.io.

@DeboBuiz
Copy link
Author

DeboBuiz commented Dec 16, 2020 via email

@DeboBuiz
Copy link
Author

DeboBuiz commented Dec 16, 2020

@Neelam-Nishad I have pasted the complete demo code here. you may copy this to you local file and test it out.

<html lang="en">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">

    <title>Hello, world!</title>
  </head>

  <body data-bs-spy="scroll" data-bs-target="#navbarResponsive" data-bs-offset="100" style="padding-top: 100px;">
    <nav class="navbar navbar-light navbar-expand-lg fixed-top bg-light">
      <div class="container-fluid">
        <a class="navbar-brand" href="#page-top">
          DEMO
        </a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
          <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarResponsive">
          <ul class="navbar-nav ms-auto me-2">
            <li class="nav-item"><a class="nav-link " href="#item1">Item 1</a></li>
            <li class="nav-item"><a class="nav-link " href="#item2">Item 2</a></li>
            <li class="nav-item"><a class="nav-link " href="#item3">Item 3</a></li>
            <li class="nav-item"><a class="nav-link " href="#item4">Item 4</a></li>
            <li class="nav-item"><a class="nav-link " href="#item5">Item 5</a></li>
          </ul>

        </div>
      </div>
    </nav>
    
    <section class="border border-5 bg-warning w-100 text-center display-1" id="item1" style="height: 500px;">Item 1</section>
    <section class="border border-5 bg-info  w-100 text-center display-1" id="item2" style="height: 500px;">Item 2</section>
     <section class="border border-5 bg-warning w-100 text-center display-1" id="item3" style="height: 500px;">Item 3</section>
     <section class="border border-5 bg-info w-100 text-center display-1" id="item4" style="height: 500px;">Item 4</section>
     <section class="border border-5 bg-warning w-100 text-center display-1" id="item5" style="height: 200vh;">Item 5</section>

    <!-- Optional JavaScript; choose one of the two! -->

    <!-- Option 1: Bootstrap Bundle with Popper -->
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script>
  </body>
  </html>```

@ghost
Copy link

ghost commented Dec 18, 2020

Same thing happens with me!

@aribudin
Copy link

Same problem
if you scroll down (content 3 and navlink 3) and reload the page, after reloading the page scroll again to top. You will get the problem ( content 1 in navlink 3 )

@harnishdesign
Copy link

I am working on my project for migration v4 to v5. This is same happens with me in v5 after reloading the page scroll again. It was working fine in v4.

@sormiston
Copy link

This is not isolated. Scrollspy is extremely buggy.

@aribudin
Copy link

I hope this problem will fixed in stable release version

@ghost
Copy link

ghost commented Dec 24, 2020

This is not isolated. Scrollspy is extremely buggy.

Exactly! Scrollspy has like 170 issues currently which have not been resolved. Normal scrollspy (not using bs4 to do them) might be a better option. Only that when working with BS you can't use normal scrollspy because of overrides!

If you migrate your project to v5 it may not happen, although I am not sure of this

@ghost
Copy link

ghost commented Dec 24, 2020

I am pretty sure this is actually a problem with the javascript of bootsrap scrollspy check #32600 about the same. Shows in depth details about the javascript and source code. I might legit fork this and correct and make some changes.

@ghost
Copy link

ghost commented Dec 24, 2020

So basically, the last refreshed item when reloaded gets highlighted. This is because bootstrap logs the last scroll and puts a certain item in the navbar (class: nav-item) with it! Then sees last scroll made and highlights the nav-item which is paired with it.

The javascript has to be reworked on because, scrollspy is very glitchy.

@mdo mdo added the js label Jan 13, 2021
@AvWoN
Copy link

AvWoN commented Jan 17, 2021

So basically, the last refreshed item when reloaded gets highlighted. This is because bootstrap logs the last scroll and puts a certain item in the navbar (class: nav-item) with it! Then sees last scroll made and highlights the nav-item which is paired with it.

I don't think this is the issue. From inspecting the scrollspy element, to me it looks like target offsets (stored in '_offsets') are recalculated from the current scroll position on page load instead of from the 0 scroll position, so when the page loads with a scroll position that is not the top, or 0, it will calculate faulty offsets. Didn't look into the code but I am assuming its because the code to get elements offset on page (which i think is deperecated code) was replaced with code like .getBoundingClientRect().top (which i think is new standard and more accurate) but which gets relative offset to current scroll. The problem is that we do not stick to either scheme (we either have to stick to current relative window offset solution or the old scroll page offset solution)

The hot fix seems extremely simple, when computing offsets, simply add current scroll position to the relative offset value we have right now. For example, we load/reload page into scroll position 1000, we have a elements at page offsets [200, 600, 1500], but does computed offset will be [-800, -400, 500] so simply add the current position and you will get the original values.

function hotfixScrollSpy() {
    var dataSpyList = [].slice.call(document.querySelectorAll('[data-bs-spy="scroll"]'))
    let curScroll = getCurrentScroll();
    dataSpyList.forEach(function (dataSpyEl) {
        let offsets = bootstrap.ScrollSpy.getInstance(dataSpyEl)['_offsets'];
        for(let i = 0; i < offsets.length; i++){
            offsets[i] += curScroll;
        }
    })
}

function getCurrentScroll() {
    return window.pageYOffset || document.documentElement.scrollTop;
}

window.onload = function () {
    hotfixScrollSpy();
}

Im fairly new to web dev so might be wrong about some things. Also one issue is that the initial header is going to be calculated wrong but as soon as you scroll it will correct itself, this is just a quick hotfix, not a solution, but this might lend someone to implement a solution within bootstrap itself.

@DigiLive
Copy link

Im fairly new to web dev so might be wrong about some things. Also one issue is that the initial header is going to be calculated wrong but as soon as you scroll it will correct itself, this is just a quick hotfix, not a solution.

Chapeau on finding this, especially when stating yourself as fairly new.
I'm gonna use this for now and scroll the page 1px with java script to 'fix' the remaining issue.

window.onload = function () {
    hotfixScrollSpy();
    window.scrollBy(0,1);
}

Hopefully the developers will take notice and fix the bootstrap component soon.

@UnHumbleBen
Copy link

UnHumbleBen commented Mar 20, 2021

@AvWoN @DigiLive thanks for finding a hotfix!

The solution makes sense in principle, but I'm running into an issue with the getInstance function, which is returning null. Does getInstance fail if Scrollspy element is set to <body>? Did you encounter this issue?

Seems like I am not the only one with this problem

Update
I was able to fix my problem by calling ScrollSpy explicitly before the hotfix

window.onload = function () {
  new bootstrap.ScrollSpy(document.body, {
    target: '#nameOfScrollSpyTargetClass'
  });  hotfixScrollSpy();
  window.scrollBy(0,1);
}

@DigiLive
Copy link

I'm no expert either, but this is my 5 cents...

getInstance returns null when the bootstrap library is not finished with loading while calling it.
I guess if you make the call manually at some later moment, it doesn't return null.

At first I tried to apply the hotfix at $(function(){}); (document ready), which fires at DOM ready, but it is to soon.
DOM loading is finished before content loading, hence window.onload or $(window).on('load', function() {});.

@PetersOtto
Copy link

Same problem here. The hotfix do not solve the problem for me.

@UnHumbleBen
Copy link

Same problem here. The hotfix do not solve the problem

What exactly is the problem with the hot fix? Is there an error in the console?

@PetersOtto
Copy link

PetersOtto commented Mar 21, 2021

Hello UnHumbleBen,
no, there are no errors in the console.

On my website scrollspy is working fine. However when I resize the browser, scrollspy crashes.
When I scroll to the top of the page and reload it, it works again (so long I resize the window)

Have a look to the Screenshot. Active is "Referenzen" but "Home" should be active.
ScrollSpyBs5

In my opinion that is exactly the problem that is described above.

I use the following js code but it does not work.

function hotfixScrollSpy() {
  var dataSpyList = [].slice.call(document.querySelectorAll('[data-bs-spy="scroll"]'))
  let curScroll = getCurrentScroll();
  dataSpyList.forEach(function (dataSpyEl) {
      let offsets = bootstrap.ScrollSpy.getInstance(dataSpyEl)['_offsets'];
      for(let i = 0; i < offsets.length; i++){
          offsets[i] += curScroll;
      }
  })
}

function getCurrentScroll() {
  return window.pageYOffset || document.documentElement.scrollTop;
}

window.onload = function () {
  new bootstrap.ScrollSpy(document.body, {
    target: '.navbar',
    offset: 20
  });  hotfixScrollSpy();
  window.scrollBy(0,1);
}

@UnHumbleBen
Copy link

@PetersOtto

Same issue. The hotfix only corrects offsets when page is reloaded but not when it is resized. Been trying to come up with with a hotfix for resizing but no luck so far

@DigiLive
Copy link

Since the issue was about a reload, it doesn't take a resize in consideration.
Maybe you can use the window.resize event to refresh the spies and apply the hotfix again.

$(window).resize(function() {
  'use strict';
  const dataSpyList = [].slice.call(document.querySelectorAll('[data-bs-spy="scroll"]'));
  dataSpyList.forEach(function(dataSpyEl) {
    bootstrap.ScrollSpy.getInstance(dataSpyEl).refresh();
  });
  hotfixScrollSpy();
  window.scrollBy(0, 1);
});

@harnishdesign
Copy link

harnishdesign commented Mar 30, 2021

Issue is still in bootstrap-5-beta-3. have you finally any fixed by officially ? @mdo

@mw-itworks
Copy link

Using Bootstrap 5.1 I also stumbled over this issue.

To get the suggested hotfixScrollSpy function (including the window.scrollBy(0,1) added by unHumbleBen) to work, I have simply postponed calling hotfixScrollSpy() using setTimeout - a delay of 50 ms worked for me.

Maybe this helps others here as well...

@ali-dev-code
Copy link

I am also having issue with bootstrap 5.0.1 scroll spy with list group,
when I refresh the page scroll spy the active wrong links.
Please solve it? anyone has solved it?
Thanks

@enkracken
Copy link

I am also having issue with bootstrap 5.0.1 scroll spy with list group,
when I refresh the page scroll spy the active wrong links.
Please solve it? anyone has solved it?
Thanks

I think 5.0.1 version is more stable than 5.0.0 or beta version. Have you tried scrollspy on 5.0.0 version too? I don't have the CDN (css & js) links. So, i hope someone can help me here

@ali-dev-code
Copy link

I tried bootstrap 4.6. Scrollspy works perfectly with 4.6.

@Kim-Theng
Copy link

Kim-Theng commented Jun 23, 2021

I tried bootstrap 4.6. Scrollspy works perfectly with 4.6.

Thank you it work for me too

@IllyaO
Copy link

IllyaO commented Sep 14, 2021

I tried bootstrap 5.1.1. Seems it's fixed.

@AvWoN
Copy link

AvWoN commented Sep 14, 2021

I tried bootstrap 5.1.1. Seems it's fixed.

FINALLY, can confirm, only took 9 months :). Thank you @alpadev , much appreciated!

@umer936
Copy link

umer936 commented Mar 25, 2022

This still appears to be the case for me on 5.1.3

On page load, it highlights the last nav-item

EDIT: Nevermind. I accidentally had body and another element having the data-bs-spy attribute. Removing from the element and only having on body fixed the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

17 participants