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 not behaving correctly #36431

Open
omar-abdul opened this issue May 24, 2022 · 36 comments
Open

ScrollSpy not behaving correctly #36431

omar-abdul opened this issue May 24, 2022 · 36 comments
Labels

Comments

@omar-abdul
Copy link

Scroll spy on body element will only activate the first item on the nav menu and will not update accordingly, Boostrap 5.1.3 works fine but for some reason 5.2 beta doesn't

@crftwrk
Copy link
Contributor

crftwrk commented May 24, 2022

Scrollspy changed completely in 5.2. I had the same problem and figured out that Scrollspy must be added to the wrapping element of the sections. This is usually NOT the <body>.

<body>
  <main>
    <div class="wrapper"> <!-- Add Scrollspy here -->
      <section id="one">
        ...
      </section>
      <section id="two">
        ...
      </section>
      <section id="three">
        ...
      </section>
    </div>
  </main>
</body>

@mdo
Copy link
Member

mdo commented May 24, 2022

Bug reports must include a live demo of the issue. Per our contributing guidelines, please create a reduced test case via CodePen or JS Bin and report back with your link, Bootstrap version, and specific browser and operating system details.


This is a saved reply.

@github-actions
Copy link
Contributor

Hello @omar-abdul. Bug reports must include a live demo of the issue. Per our contributing guidelines, please create a reduced test case on CodePen or JS Bin and report back with your link, Bootstrap version, and specific browser and Operating System details.

@mastercoms
Copy link
Contributor

mastercoms commented May 29, 2022

Scrollspy has clearly regressed, even on the docs. The prior behavior was to make a link active if its associated element reached the scroll top. Now, it seems to move the active state if one element is more visible than the other. This does not get the same results, and has very strange behavior in various different scenarios which expected the previous behavior. This is just the coded behavior, it's not browser dependent.

Here is an example of the difference on the docs page itself.

scrollspy.mp4

@mastercoms
Copy link
Contributor

mastercoms commented May 29, 2022

Here's one example of how it broke. This is just me making the paragraph longer on the 5.2 docs.

scrollspy-long-section-heading.mp4

There's just no active link, since the heading is not visible. There is absolutely no way to handle this correctly with the current implementation, since if you have a wrapper element around each heading and paragraph, then it does not activate properly at all (since the large wrapper element will not satisfy the thresholds properly). And you can fiddle around with the root margin and maybe thresholds to help fit the content within the intersection parameters, but it will be specific to each section, so there is no setup that works with varying bits of spied content sections.

@tranqt1984
Copy link

tranqt1984 commented Jun 10, 2022

I have the same issue with version 5.2 regarding the ScrollSpy skipping to the newly observed element when scrolling. This does not provide a good user experience and will confuse the user as they are scrolling.

In this video, I updated the height from 200px to 600px which shows the same issue I'm having when using the ScrollSpy.

Screen.Recording.2022-06-10.at.12.18.10.PM.mov

POC: IntersectionObserver observing the element that is being scrolled into the viewport which mimics the ScrollSpy buggy behavior. https://codepen.io/byondsick12/pen/oNEdqzW

POC: This is my current solution... https://codepen.io/byondsick12/pen/wvyNqqd?editors=0111

@github-actions
Copy link
Contributor

As the issue was labeled with awaiting-reply, but there has been no response in 14 days, this issue will be closed. If you have any questions, you can comment/reply.

@julien-deramond
Copy link
Member

Reopened it since a CodePen has been provided by @tranqt1984 in the meantime (haven't checked its content yet)

@kenrobbins
Copy link

I noticed this issue as well. I have little experience with JS, but from what I read, it seems like with the smallest threshold at 0.1, that means that 10% of an element has to be inside the observer rectangle before the callback is called. That means if an element is more than 10x the height of the observer rectangle, this will never occur, which is why making the paragraph longer broke it.

Regarding the other issue with increasing the height of the spied element causing the spy to jump from 1 to 4 and then from 5 to 2, the observed rectangle is big enough that multiple elements are already in the observer rectangle, so the next one to enter it is not always the one immediately after the active element.

To solve this, I made the threshold 0 so that when any pixel enters the observer rectangle, the callback is called. To avoid multiple elements being inside the observer rectangle, I made the observer rectangle quite small and made the content elements have a minimum height. I placed the observer rectangle just below the top of the spied element. Whatever element is in this area should be highlighted. To do that, I set the root margin to -10% 0px -70%. I also set the minimum height of the content elements to half of the spied element. Depending on use case, I'm sure there are other techniques to accomplish the same.

Anyway, my hacky solution (again, I'm not a frontend programmer) is:

spy-container {
  min-height: calc(100vh / 2.0);
}
<main ... data-bs-root-margin="-10% 0px -70%">
  <div class="spy-container"></div>
  <div class="spy-container"></div>
  ...
    ss = bootstrap.ScrollSpy.getInstance("main");
    ss._getNewObserver = () => { 
      const options = {
        root: ss._rootElement,
        threshold: [0],
        rootMargin: ss._getRootMargin()
      };
      return new IntersectionObserver(entries => ss._observerCallback(entries), options);
    }
    ss._observer = null;
    ss.refresh();

@GeoSot
Copy link
Member

GeoSot commented Jul 16, 2022

@kenrobbins #36750 this may help you to use threshold properly

@Khaldei
Copy link

Khaldei commented Aug 2, 2022

There are additional pieces that are not working here. They are visible directly on the demo site. For example, navigate to...

https://getbootstrap.com/docs/5.0/components/scrollspy/#item-1-2

Scroll to "Example with nested nav"

You will notice that the scrollspy navbar is now off by two elements.

"Item 1" in the navbar is now linked to "Item 1-2"
image

If you scroll up to the "Item 1", nothing at all is highlighted.
image

And if you scroll down, the offsets continue.
image

@qofe
Copy link

qofe commented Sep 14, 2022

Posting in case it helps anyone else -- As per the original post about Scrollspy not working on the 'body' element in 5.2. I was going through the steps to make a code-pen and found through that exercise it worked just fine. My issue was a conflict I had in the 'overflow' attribute on the 'body'. Once I cleared that up, it worked as expected.

@shikkaba
Copy link

My issue was a conflict I had in the 'overflow' attribute on the 'body'. Once I cleared that up, it worked as expected.

@qofe Can you elaborate on this?

@iMattPro
Copy link

iMattPro commented Dec 15, 2022

Is there any update on this? Scrollspy and the Docs are awful now. Before Scrollspy when spying on a list of links, would highlight when the target reached the top of the viewport, but now it highlights just when the target is visible in the window anywhere. There's absolutely no guidance on how to tweak these root margins and thresholds to at least recreate the original scroll spy behavior from before 5.2 where it highlights when the target is at the top of the viewport.

@usa-usa-usa-usa
Copy link

I am experiencing the same issue. If a section is too short to consume enough of the vieweport, the nav item is never actually navigating.

I'd prefer that whatever is at the top of the screen is what is deemed "active".

@SekarMurugesan
Copy link

same issue i have in react when moving downwards it working but moving upward it is not working

@suhailkc
Copy link

Same issue. ScrollSpy is not working on

@marcus-at-localhost
Copy link

@shikkaba I experienced the same as @qofe.
The issue, described in the very first post, is having something like this:

body{
	overflow-x: hidden; /* or auto or scroll */
}

and it was resolved by doing something like this:

body{
	overflow-x: unset; /* or initial */
}

I don't say that's generally the case for this not working, but overflow is likely the issue.

@leroynas
Copy link

leroynas commented Jan 24, 2023

I am actually working on a custom scroll-spy for a company. I was trying to get inspiration by checking the bootstrap implementation, however I found out that the IntersectionObserver only returns entries that changed in visibility. However it can be that there are 10 entries visible at load. These entries are processed, and the first entry is visible (almost always during load). When you scroll down, only entries that change in visibility are available so 11, then 12, then 13 (with entry.isIntersecting true). And likewise elements 1, then 2, then 3 (with entry.isIntersecting false).

On page load

|--------------------------------------------------|
|  item 1                                          |
|  item 2                                          |
|  item 3                                          |
|  item 4                                          |
|--------------------------------------------------|
   item 5
   item 6

new IntersectionObserver((entries) => {
  console.log(entries);
});

// Result: [
//   { id: 1, isIntersecting: true, ... },
//   { id: 2, isIntersecting: true, ... },
//   { id: 3, isIntersecting: true, ... },
//   { id: 4, isIntersecting: true, ... },
//   { id: 5, isIntersecting: false, ... },
//   { id: 6, isIntersecting: false, ... },
// ]

When scrolling to where item 6 is visible and item 1 isn't will result in this

   item 1
|--------------------------------------------------|
|  item 2                                          |
|  item 3                                          |
|  item 4                                          |
|  item 5                                          |
|--------------------------------------------------|
   item 6


new IntersectionObserver((entries) => {
  console.log(entries);
});

// Result:

// Result: [
//   { id: 1, isIntersecting: false, ... },
//   { id: 6, isIntersecting: true, ... },
// ]

This will trigger item 6 to be active where actually item 2 is the first visible item.

This means that solely depending on the IntersectionObserver is not possible. Also when there is nog change in visible elements it can be possible that another scroll-spy target should be made active.

A possible correct implementation would be to combine the IntersectionObserver with a onscroll where the IntersectionObserver is used to save all visible elements in some array/object and the onscroll callback uses these visible elements to determine which one is the first visible (of course only intersecting elements can be active), I expect this first option will reduce the load of the onscroll callback. Or only using a onscroll (basically going back to version 5.1).

Hope this can be of any help. I could share the vue composable implementation when I'm done however this will have to be adjusted for the needs of bootstrap (just inspiration).

buepro added a commit to buepro/typo3-pizpalue that referenced this issue Jan 27, 2023
Description
~~~~~~~~~~~

Since the bootstrap scroll spy is buggy a replacement
has been introduced, dropping the typoscript configuration
constant `pizpalue.menu.scroll.offset` in favor of
`pizpalue.menu.scroll.rootMargin`.

Corrective action
~~~~~~~~~~~~~~~~~

Review the typoscript scroll configuration.

Related: twbs/bootstrap#36431
@qofe
Copy link

qofe commented Mar 8, 2023

My issue was a conflict I had in the 'overflow' attribute on the 'body'. Once I cleared that up, it worked as expected.

@qofe Can you elaborate on this?

Yes, I had
`body {overflow-x:hidden; }
in top of my styles. Removed overflow setting, changed nothing else, and that eliminated the scrollspy issue described in the first post.

@davidtmiller
Copy link

davidtmiller commented Mar 21, 2023

I have been getting better results when wrapping the entire section that I want to spy on with the id target.

For example, instead of doing this (example given in docs):

<h4 id="scrollspyHeading1">First heading</h4>
<p>...</p>

I will do this:

<div id="scrollspyHeading1">
    <h4>First heading</h4>
    <p>...</p>
</div>

This way the id target is in the viewport for most of the time. This does seem to mess things up a bit when I have one section that is smaller than another that is also within the viewport.

My implementation is very similar to the old Bootstrap 3 docs with nested nav items that appear when the parent has the active state (like the sidebar here https://getbootstrap.com/docs/3.4/components/) making it very important to have the parent being spied on at all time.

I played around with the rootMargin property as well, similar to what was done in the docs, bringing it to rootMargin: '0px 0px -35%' in my case which seemed to help.

Everything seems to be working when scrolling down, but when reaching the bottom of the page and scrolling back up, there are a lot of issues. The active item spied, when clicking on a menu link, after getting to the bottom of the page is inaccurate and sometimes no active item is picked up. Some items don't receive the active state at all, especially when scrolling back up the page versus clicking on a menu link. I am thinking that this has to do with the last scrollspy target section being very short in height, so it messes with the positioning when going back up.

Just some observations and suggestions in case they help progress this issue further!

@Fatbat
Copy link

Fatbat commented Apr 24, 2023

I have been getting better results when wrapping the entire section that I want to spy on with the id target.

Yeah, I wrap the content with a section that references each scroll spy target and it doesn't help. The "Home" entry isn't working either. This has been broken for a while now.

yashi added a commit to yashi/rtd-test that referenced this issue May 12, 2023
Add Bootstrap 3.4.1 along with the dependency, jquery 3.7.

Newer Bootstrap doesn't have working Scrollspy[1].  Thus we are using
v3.4.1.

[1]: twbs/bootstrap#36431

Signed-off-by: Yasushi SHOJI <[email protected]>
yashi added a commit to spacecubics/scobc-fpga-technical-reference-manual that referenced this issue May 12, 2023
Add Bootstrap 3.4.1 along with the dependency, jquery 3.7.

Newer Bootstrap doesn't have working Scrollspy[1].  Thus we are using
v3.4.1.

[1]: twbs/bootstrap#36431

Signed-off-by: Yasushi SHOJI <[email protected]>
@iMattPro
Copy link

So this issue just being tossed aside?

@julien-deramond
Copy link
Member

So this issue just being tossed aside?

It's not being tossed aside. We're focusing right now on the v5.3.0 release. Then we'll try to define and prioritize what comes next. I'll keep in mind this one. Please understand that there's a huge amount of work to do in Bootstrap, and everybody does it in their spare time.

@itchyny
Copy link

itchyny commented Jun 15, 2023

In my use case, ScrollSpy does not work with sections with large height by default, and l noticed that the default threshold [0.1, 0.5, 1], typically 0.1, is not good for large height section . For me, data-bs-threshold="0,1" data-bs-root-margin="-30% 0% -70%" fixed the issue. (EDIT: Adding display: flow-root to the sections is also important to disable gaps between sections caused by child margins.)

@gettonet
Copy link

gettonet commented Jul 2, 2023

Is there any update on this? Scrollspy and the Docs are awful now. Before Scrollspy when spying on a list of links, would highlight when the target reached the top of the viewport, but now it highlights just when the target is visible in the window anywhere. There's absolutely no guidance on how to tweak these root margins and thresholds to at least recreate the original scroll spy behavior from before 5.2 where it highlights when the target is at the top of the viewport.

I completely agree and have the same issue. Following the topic...

@iMattPro
Copy link

iMattPro commented Sep 4, 2023

So this issue just being tossed aside?

It's not being tossed aside. We're focusing right now on the v5.3.0 release. Then we'll try to define and prioritize what comes next. I'll keep in mind this one. Please understand that there's a huge amount of work to do in Bootstrap, and everybody does it in their spare time.

All I meant was, this Issue is no longer assigned to any project, so I see this falling through the cracks. It's already been an open issue and a problem for 15 months.

@M-Yankov
Copy link

M-Yankov commented Jan 24, 2024

Another example that scrollspy is not working in Bootstrap 5.3.2
Windows 10 64-bit Firefox 112.0, (MS Edge 120.0.2210.144)
https://jsfiddle.net/2oxrkzyb/

The "Two" options doesn't get activated.

If you swap resources to 5.1.3 it's working fine.

@bytes-commerce
Copy link

What I figured is that the ScrollSpy seems to work when wrapping the seciont and making clear sections with that.

However, especially in responsive view (iPhone - roughly 400px) - it seems that very large sections are again not properly tracked by ScrollSpy. I am just checking out if it is an issue with the zIndex but that'd surprise me as of now.

For example my whole page has a total height of 16630 px, and the largest section clocks in with 7350 pixels. I'd add screenshots but its not giving much information in responsive view - one time the active class is added to a nav, one time it isn't.

@peterstavrou
Copy link

Has anyone managed to fix this? Even on the documentation page for v5.3 it's not working as it should.

@cwildfoerster
Copy link

any updates on this?

@joshvillarreal
Copy link

I am having similar issues!

@sohammondal
Copy link

Hello everyone, I am using this feature in a Nextjs SSR app and it was working sporadically for me. I had initially implemented it using the #via-data-attributes approach.

However when I switched initialisation to the #via-javascript approach it worked fine consistently.

On the client side, if you do something like this, it should work for you as well -

  useEffect(() => {

    import("bootstrap").then((bootstrap) => {
      new bootstrap.ScrollSpy("#sections-wrapper", {
        target: ".sidebar-nav",
      });
    });

  }, []);

If you still prefer the #via-data-attributes approach, then you would need to run getOrCreateInstance on the client side to get it working.

In general, its not a hack but an alternate approach as mentioned in the official documentation.

@frontenddevguy
Copy link

I also had to wrap the section in a div with the target id, rather than rely on headings being correctly detected.
That improves the behaviour somewhat, and avoids there being 'dead space' between highlighted items when there are long chunks of text.

However, the position of scroll where the id is detected seems erratic, and even depends on speed of scroll.

@frontenddevguy
Copy link

So this issue just being tossed aside?

It's not being tossed aside. We're focusing right now on the v5.3.0 release. Then we'll try to define and prioritize what comes next. I'll keep in mind this one. Please understand that there's a huge amount of work to do in Bootstrap, and everybody does it in their spare time.

Please just revert it back to the previous working version

@Fatbat
Copy link

Fatbat commented May 17, 2024

So this issue just being tossed aside?

It's not being tossed aside. We're focusing right now on the v5.3.0 release. Then we'll try to define and prioritize what comes next. I'll keep in mind this one. Please understand that there's a huge amount of work to do in Bootstrap, and everybody does it in their spare time.

It has now been more than two years.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: To do
Status: To do
Development

No branches or pull requests