skip to Main Content

I’m trying to move the 3 scrollbar from 3 <div class="container"> by the class name with the same event at one time. This is my approach:
In first place I share the event handler from the script

function handleScroll(event: any) {
  let position: number = event._vts
  let scrollOptions = {
    left: position,
    top: 0,
    behavior: undefined
  }

  if (event._vts) {
    if (graficas.length !== 0) {
      console.log('muevo el array')
      console.log('asi tengo el array -> ' + graficas)
      const el0 = document.getElementsByClassName('container').item(0)
      el0?.scroll(scrollOptions)
      const el1 = document.getElementsByClassName('container').item(1)
      el1?.scroll(scrollOptions)
      const el2 = document.getElementsByClassName('container').item(2)
      el2?.scroll(scrollOptions)
    } else {
      console.log('lleno el array')
      for (let index = 0; index < document.getElementsByClassName('container').length; index++) {
        graficas.push(document.getElementsByClassName('container')[index])
        document.getElementsByClassName('container')[index].scrollBy(event._vts, 0)
        console.log(document.getElementsByClassName('container')[index])
      }
      graficas.shift()
      console.log('asi queda el array -> ' + graficas)
    }
  }
}  

then I share the portion of template that display the chart with the scrollbars I want to move:

  <h2>CPU</h2>
    <div class="container" @scroll="handleScroll">
      <div class="containerBody">
        <Bar :data="cpu" :options="options" />
      </div>
    </div>
    <h2>IO</h2>
    <div class="container" @scroll="handleScroll">
      <div class="containerBody">
        <Bar :data="io" :options="options" />
      </div>
    </div>
    <h2>SEC</h2>
    <div class="container" @scroll="handleScroll">
      <div class="containerBody">
        <Bar :data="sec" :options="options" />
      </div>
    </div>

Any idea how can I approach to achieve this?

2

Answers


  1. Approach 1: Instead of scroll events, listen for wheel event.

    Mostly users scroll by gestures or mouse wheel. With this you can easily avoid infinite loop (one scroll triggering other and vise-versa). This will not work when the user scrolls by dragging the scroll handle.

    Approach 2: Listen for mouseover to know which element is directly under cursor and only use scroll event of that element to sync scrolls.

    Login or Signup to reply.
  2. The following snippet is a modified/simplified version of the source code for Vue Scroll Sync. Depending on the complexity of your needs (and your ability to use third-party dependencies in your codebase), you may want to pull that library directly, rather than hard-coding the component.

    A few callouts:

    • Note the use of a uuid to identify the different regions whose scroll position should be synced. This prevents a given scroll event being "double-counted" when the other regions’ scroll positions are updated.
    • In all cases, the onScroll handler is temporarily removed from the divs when the scroll position is being modified. Again, this prevents the "double-counting" behaviors that might impact smooth scrolling.
    • The divs are styled and colored for ease of demoing, but of course, you can copy your own markup and content into the scroll-sync component to achieve the same effect. Deep nesting of DOM elements should still work, because the topNode of the component is computed on mount, and events are emitted/captured on that node.
    let uuid = 0;
    
    Vue.component('scroll-sync', {
      name: 'ScrollSync',
      template: document.querySelector("#scroll-sync-template").innerHTML,
      data() {
        return {
          topNode: null
        }
      },
      beforeCreate() {
        this.uuid = uuid.toString();
        uuid += 1;
      },
      mounted() {
        let parent = this.$parent
        while (parent) {
          this.topNode = parent
          parent = this.topNode.$parent
        }
    
        const vue = this;
        this.topNode.$on('scroll-sync', function(data) {
          if (data.emitter === vue.uuid) {
            return
          }
          const {
            scrollTop,
            scrollHeight,
            clientHeight,
            barHeight
          } = data
    
          const scrollTopOffset = scrollHeight - clientHeight
    
          vue.$el.onscroll = null
          if (scrollTopOffset > barHeight) {
            vue.$el.scrollTop = scrollTop;
          }
          window.requestAnimationFrame(() => {
            vue.$el.onscroll = vue.handleScroll
          })
        })
        this.$el.onscroll = this.handleScroll
      },
      methods: {
        handleScroll: function(e) {
          const vue = this
          window.requestAnimationFrame(() => {
            const {
              scrollTop,
              scrollHeight,
              clientHeight,
              offsetHeight
            } = e.target
    
            this.topNode.$emit('scroll-sync', {
              scrollTop,
              scrollHeight,
              clientHeight,
              barHeight: offsetHeight - clientHeight,
              emitter: vue.uuid
            })
          })
        }
      }
    });
    
    Vue.config.productionTip = false;
    
    new Vue({
      el: '#app'
    });
    .scroll-sync-container {
      height: 100%;
      width: 100%;
      position: relative;
      overflow: auto;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
    
    <div id="app">
      <div style="display:flex;">
        <scroll-sync vertical style="height:500px;width:100px;margin-right:50px;">
          <div style="width:100px;height:1000px;background:linear-gradient(red, green, blue);"></div>
        </scroll-sync>
        <scroll-sync vertical style="height:500px;width:100px;">
          <div style="width:100px;height:2000px;background:linear-gradient(red, green, blue);"></div>
        </scroll-sync>
      </div>
    </div>
    
    <div id="scroll-sync-template">
      <div class="scroll-sync-container">
        <slot></slot>
      </div>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search