Search code examples
vue.jsvuejs2bootstrap-vue

How can I listen to the scroll event in a sidebar [Bootstrap Vue]


I am using the Sidebar component from BootstrapVue to render a navigation for my application.

As an example, I have 30 menu items in my navigation, so the content within the sidebar scrolls vertically. I would like to bind to that scroll event to dynamiclly add/remove some classes.

I have tried to create a custom scroll directive:

Vue.directive('scroll', {
    inserted: function(el, binding) {
        console.log('el', el);
        let f = function(evt) {
            if (binding.value(evt, el)) {
                window.removeEventListener('scroll', f);
            }
        };

        window.addEventListener('scroll', f);
    }
});

...then register that to the component within my vue file:

<b-sidebar
   v-scroll="handleScroll"
   title="Menu"
   shadow="lg"
   backdrop
   @change="$emit('sidebar-change')"


...

handleScroll() {
      console.log('handleScroll');
},

The directive is being picked up properly, but my handleScroll method is firing when the main body is scrolling, not the sidebar.

In my directive, I am logging to see what element it thinks it's working with:

<div tabindex="-1" class="b-sidebar-outer">...</div>

Since Bootstrap is dynamiclly creating the markup for the overlay, that's the parent element -- looking closer, I believe I need to attach my directive to this:

<div class="b-sidebar-body">...</div>

That is the <div> that looks to be scrolling. However, since it is generated at runtime, I don't know how to hook into that.

I have also tried using @native.scroll="myMethod" on the component...no luck there either.

How can I listen for the scroll event within my sidebar component? Thank you for any suggestions!


Solution

  • Your scroll listener fires on the main window because the directive attached the event listener to window, and not the element.

    To listen to scroll events on the contents of b-sidebar, the listener should be on an element inside the default slot of b-sidebar (not the b-sidebar itself).

    1. Put a wrapper div inside b-sidebar's default slot, and style it to enable scrolling:

      <template>
        <b-sidebar>
          <div class="wrapper">
            <!-- your actual contents here -->
          </div>
        </b-sidebar>
      </template>
      
      <style>
      .wrapper {
        overflow: auto;
        height: 100%;
      }
      </style>
      
    2. Add the custom v-scroll directive on the wrapper div:

      <div class="wrapper" v-scroll="handleScroll">
      
    3. Update the custom directive to add the binding value as the event listener on the given element's scroll event:

      Vue.directive('scroll', (el, binding) => {
        let f = (evt) => {
          if (binding.value(evt, el)) {
            el.removeEventListener('scroll', f)
          }
        }
      
        el.addEventListener('scroll', f)
      })
      

    demo