【问题标题】:How do I synchronize the scroll position of two divs?如何同步两个 div 的滚动位置?
【发布时间】:2012-03-03 10:13:29
【问题描述】:

我希望将 2 个 div 设置为特定宽度(即 500 像素)。一个在另一个之上,水平对齐。

顶部框应隐藏其滚动条,底部应显示滚动条,当用户滚动时,我希望顶部框的偏移量更改为底部框的值。这样当底部 DIV 水平滚动时,顶部 DIV 似乎也在同步滚动。

我很高兴在 Jquery 中执行此操作,如果它使过程更容易。

【问题讨论】:

    标签: javascript jquery


    【解决方案1】:
    $('#bottom').on('scroll', function () {
        $('#top').scrollTop($(this).scrollTop());
    });
    

    在这里,我们使用.scrollTop() 尽其所能,从带有滚动条的元素中获取scrollTop 值,并为其他元素设置scrollTop 以同步它们的滚动位置:http://api.jquery.com/scrollTop

    这假定您的底部元素的 ID 为 bottom,而顶部元素的 ID 为 top

    您可以使用 CSS 隐藏 top 元素的滚动条:

    #top {
        overflow : hidden;
    }
    

    这是一个演示:http://jsfiddle.net/sgcer/1884/

    我想我从来没有真正的理由这样做,但它看起来很酷。

    【讨论】:

    • 好的很好的例子(谢谢!)。您能否提供一个类似的示例,但使用 Jquery Scroll Left 使其水平滚动?
    • @JosephU。 :),你有没有试过用scrollLeft 替换scrollTop 这两个实例?
    • @JosephU。这是一个更新的 JSFiddle,它演示了使用 scrollLeft 而不是 scrollTopjsfiddle.net/sgcer/1
    • Jasper 的优秀例子!
    • 在演示小提琴中,#bottomoverflow 应该是overflow-y,以避免在带有滚动控件的 div 底部获得 x-scrollbar。
    【解决方案2】:

    我知道这是一个旧线程,但也许这会对某人有所帮助。 如果您需要双向同步滚动,仅处理两个容器的滚动事件并设置滚动值是不够的,因为滚动事件进入循环并且滚动不平滑(尝试垂直滚动一个鼠标滚轮 Hightom 给出的例子)。

    这是一个如何检查是否已经在同步滚动的示例:

    var isSyncingLeftScroll = false;
    var isSyncingRightScroll = false;
    var leftDiv = document.getElementById('left');
    var rightDiv = document.getElementById('right');
    
    leftDiv.onscroll = function() {
      if (!isSyncingLeftScroll) {
        isSyncingRightScroll = true;
        rightDiv.scrollTop = this.scrollTop;
      }
      isSyncingLeftScroll = false;
    }
    
    rightDiv.onscroll = function() {
      if (!isSyncingRightScroll) {
        isSyncingLeftScroll = true;
        leftDiv.scrollTop = this.scrollTop;
      }
      isSyncingRightScroll = false;
    }
    .container {
      width: 200px;
      height: 500px;
      overflow-y: auto;
    }
    
    .leftContainer {
      float: left;
    }
    
    .rightContainer {
      float: right;
    }
    <div id="left" class="container leftContainer">
    Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.
    </div>
    <div id="right" class="container rightContainer">
    Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.
    </div>

    这里是fiddle

    【讨论】:

    • 完美...效果很好!谢谢。
    • 太好了,谢谢!!
    • 纯 JavaScript 解决方案。谢谢。
    • 我对水平滚动应用了相同的逻辑。效果很好。谢谢!
    【解决方案3】:

    我一直在寻找双向解决方案,感谢大家,你们的贡献帮助我做到了这一点:

    $('#cells').on('scroll', function () {
    $('#hours').scrollTop($(this).scrollTop());
    $('#days').scrollLeft($(this).scrollLeft());});
    

    在 JSFiddle 上查看:https://jsfiddle.net/sgcer/1274/

    希望有一天它对某人有所帮助:-)

    【讨论】:

      【解决方案4】:

      好的,所以我评估了这里提供的所有选项,它们都有某种形式的限制:

      • accepted answer 在使用鼠标滚轮时存在已知问题。
      • next highest upvote 没有滚轮问题,但仅适用于两个已知元素。如果您需要更多元素,它是不可扩展的。
      • 唯一显示出希望的解决方案是Lacho's,但代码中存在已知元素引用,这些引用未包含在 sn-p 中。从好的方面来说,它具有性能所需的结构,而且它在 TypeScript 中。

      我利用它创建了一个可重复使用的版本,它可以处理无限数量的元素,并且不需要 JQuery。

      function scrollSync(selector) {
        let active = null;
        document.querySelectorAll(selector).forEach(function(element) {
          element.addEventListener("mouseenter", function(e) {
            active = e.target;
          });
      
          element.addEventListener("scroll", function(e) {
            if (e.target !== active) return;
      
            document.querySelectorAll(selector).forEach(function(target) {
              if (active === target) return;
      
              target.scrollTop = active.scrollTop;
              target.scrollLeft = active.scrollLeft;
            });
          });
        });
      }
      
      //RWM: Call the function on the elements you need synced.
      scrollSync(".scrollSync");
      

      您可以在此处查看 JSFiddle:http://jsfiddle.net/gLa2ndur/3。您可以看到它同时使用了水平和垂直滚动示例。

      唯一已知的限制是它可能不适用于不同大小的 div。如果您的用例认为有必要(我的没有),我相信有人可以将Andrew's work 纳入其中。

      我希望这对某人有帮助!

      【讨论】:

      【解决方案5】:

      防止这种循环问题并实现平滑滚动的另一种解决方案。 这确保只有焦点元素获得滚动事件。

      let activePre: HTMLPreElement = null;
      document.querySelectorAll(".scrollable-pre").forEach(function(pre: HTMLPreElement) {
          pre.addEventListener("mouseenter", function(e: MouseEvent) {
              let pre = e.target as HTMLPreElement;
              activePre = pre;
          });
      
          pre.addEventListener("scroll", function(e: MouseEvent) {
              let pre = e.target as HTMLPreElement;
      
              if (activePre !== pre) {
                  return;
              }
      
              if (pre !== versionBasePre) {
                  versionBasePre.scrollTop = pre.scrollTop;
                  versionBasePre.scrollLeft = pre.scrollLeft;
              }
      
              if (pre !== versionCurrentPre) {
                  versionCurrentPre.scrollTop = pre.scrollTop;
                  versionCurrentPre.scrollLeft = pre.scrollLeft;
              }
      
          });
      });
      

      【讨论】:

      【解决方案6】:

      感谢上面的答案,我制作了一个适合我的混合解决方案:

      var isLeftScrollTopCalled = false;
      $('#leftElement').scroll(function (e) {
          if (isRightScrollTopCalled) {
              return isRightScrollTopCalled = false;
          }
      
          $('#rightElement').scrollTop($(this).scrollTop());
          isLeftScrollTopCalled = true;
      });
      
      var isRightScrollTopCalled = false;
      $('#rightElement').scroll(function (e) {
          if (isLeftScrollTopCalled) {
              return isLeftScrollTopCalled = false;
          }
      
          $('#leftElement').scrollTop($(this).scrollTop());
          isRightScrollTopCalled = true;
      });
      

      【讨论】:

        【解决方案7】:

        我注意到他的问题已经很老了,但我认为我可以留下一个比使用计时器更好的 jQuery 实现。在这里,我使用两个事件侦听器,就像以前使用的解决方案一样,但是,这将使用比例/百分比来同步两个不同大小的 div 框的滚动。您可以应用相同的知识来获取滚动条到 Vanilla JS 解决方案的位置。这将使两个 div 之间的滚动更加顺畅。

        /** Scroll sync between editor and preview **/
        // Locks so that only one pane is in control of scroll sync
        var eScroll = false;
        var pScroll = false;
        // Set the listener to scroll
        this.edit.on('scroll', function() {
            if(eScroll) { // Lock the editor pane
                eScroll = false;
                return;
            }
            pScroll = true; // Enable the preview scroll
        
            // Set elements to variables
            let current = self.edit.get(0);
            let other = self.preview.get(0);
        
            // Calculate the position of scroll position based on percentage
            let percentage = current.scrollTop / (current.scrollHeight - current.offsetHeight);
        
            // Set the scroll position on the other pane
            other.scrollTop = percentage * (other.scrollHeight - other.offsetHeight);
        });
        this.preview.on('scroll', function() {
            if(pScroll) { // Lock the preview pane
                pScroll = false;
                return;
            }
            eScroll = true; // Enable the editor scroll
        
            // Set elements to variables
            let current = self.preview.get(0);
            let other = self.edit.get(0);
        
            // Calculate the position of scroll position based on percentage
            let percentage = current.scrollTop / (current.scrollHeight - current.offsetHeight);
        
            // Set the scroll position on the other pane
            other.scrollTop = percentage * (other.scrollHeight - other.offsetHeight);
        
        });
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-12-22
          • 2012-08-15
          • 1970-01-01
          • 2015-04-07
          • 1970-01-01
          • 2010-10-20
          • 2021-04-10
          相关资源
          最近更新 更多