将滚动条div同步到非滚动的div

时间:2014-02-19 19:56:14

标签: javascript jquery html safari scroll

我有一个网页,其中有三个div同步在一起。

    +----------+
    |   TOP    |
+---+----------+
|   |         ||
| L | CENTER  ||
|   |_________||
+---+----------+

<!--Rough HTML-->
<div>
  <div class="topbar-wrapper">
    <div class="topbar"></div>
  </div>
  <div class="container">
    <div class="sidebar-wrapper">
      <div class="sidebar"></div>
    </div>
    <div class="center-wrapper"> <!-- Set to the window size & overflow:scroll -->
      <div class="center"></div> <!-- Has the content -->
    </div>
  </div>
</div>

中心div有滚动条,水平和垂直。顶部和左侧div没有滚动条。相反,当移动水平滚动条时,滚动事件会相应地更新顶部div margin-left。同样,当移动垂直滚动条时,margin-top会更新。在这两种情况下,它们都设置为负值。

$('.center-wrapper').scroll(function(e) {
  $('.sidebar').css({marginTop: -1 * $(this).scrollTop()});
  $('.topbar').css({marginLeft: -1 * $(this).scrollLeft()});
});

这在Chrome和Firefox中运行良好。但在Safari中,移动滚动条与正确设置的负边距之间存在延迟。

有更好的方法吗?或者有没有办法摆脱Safari的滞后?

编辑:

查看这个小提琴,看看Safari中的滞后:http://jsfiddle.net/qh2K3/

4 个答案:

答案 0 :(得分:12)

如果this JSFiddle效果更好,请告诉我。我也经历了同样的“滞后”(OS X上的Safari 7)和这些小的CSS更改显着改进了它。我最好的猜测是,Safari很懒,并没有开启其高性能电机。我们可以使用一些简单的CSS强制Safari启用它:

.topbar-wrapper, .sidebar-wrapper, .center-wrapper {
    -webkit-transform: translateZ(0);
    -webkit-backface-visibility: hidden;
    -webkit-perspective: 1000;
}

通过将部分工作卸载到GPU,可以在Safari中启用hardware accelerated CSS。它改进了浏览器中的渲染,这可能是延迟中的问题。

答案 1 :(得分:3)

我已经有过几次这个问题了,当基于这样的事件逐步移动元素时,safari似乎滞后了。

以下是我过去解决问题的方法。

在支持硬件加速的浏览器中,使用任何“3d”CSS属性将启用硬件acc。

如果支持3D变换,我们也可以使用translate3d来移动元素,因为这样可以比移动像素更平滑。

这意味着我们首先必须找出CSS转换属性的正确前缀,然后检查对3D转换的支持。如果我们有支持,翻译元素,如果不是移位像素。

通过计算没有库的前缀和3D支持,它应该尽可能快,并且没有依赖性。

var parent     = document.querySelector('.center-wrapper'),
    sidebar    = document.querySelector('.sidebar'),
    topbar     = document.querySelector('.topbar'),
    has3D      = false,
    transform  = null,
    transforms = {
            'webkitTransform' : '-webkit-transform',
            'OTransform'      : '-o-transform',
            'msTransform'     : '-ms-transform',
            'MozTransform'    : '-moz-transform',
            'transform'       : 'transform'
    };

for (var k in transforms) if (k in parent.style) transform = k;

if ( transform !== null ) {
    sidebar.style[transform] = 'translate3d(0,0,0)'; // start hardware acc
    topbar.style[transform]  = 'translate3d(0,0,0)'; // start hardware acc
    // check support
    var s = window.getComputedStyle(sidebar).getPropertyValue(transforms[transform]);
    has3D = s !== undefined && s.length > 0 && s !== "none";
}

if (has3D) {
    parent.onscroll = function (e) {
        sidebar.style[transform] = 'translate3d(0px, '+ (this.scrollTop * -1) +'px, 0px)';
        topbar.style[transform]  = 'translate3d('+ (this.scrollLeft * -1) +'px, 0px, 0px)';
    }
}else{
    parent.onscroll = function (e) {
        sidebar.style.marginTop = (this.scrollTop  * -1) + 'px';
        topbar.style.marginLeft = (this.scrollLeft * -1) + 'px';
    }
}

FIDDLE

请记住将它放在DOM中的元素之后,如</body>之前,或者如果不可能,则必须将其包装在DOM就绪处理程序中。

作为旁注,IE7及以下版本不支持querySelector,如果这是一个问题,你必须将其填充。

答案 2 :(得分:2)

缓存选择器将有所帮助。

更改:

$('.center-wrapper').scroll(function(e) {
  $('.sidebar').css({marginTop: -1 * $(this).scrollTop()});
  $('.topbar').css({marginLeft: -1 * $(this).scrollLeft()});
});

收件人:

var _scroller = $('.center-wrapper'),
    _swrapper = $('.sidebar-wrapper'),
    _twrapper = $('.topbar-wrapper');

_scroller.scroll(function (e) {
    _swrapper.scrollTop(_scroller.scrollTop());
    _twrapper.scrollLeft(_scroller.scrollLeft());
});
如果必须每次都重新创建jquery对象.scroll()

$(this)将会非常努力。

昨晚在评论中写了这篇文章,虽然还没有测试,但今天实际上觉得应该添加这与性能直接相关。

我们可以尝试一下 - here


更多/方法 - 让它们成为原生

还有另外一个我们可以删除的jquery方法调用。 .scrollTop().scrollLeft()通过缓存并返回原始_scrollernative = _scroller.get(0)来使用随时可用的属性scrollLeft

赞:

var _scroller = $('.center-wrapper'),
    _scrollernative = _scroller.get(0),
    _swrapper = $('.sidebar-wrapper').get(0),
    _twrapper = $('.topbar-wrapper').get(0);

_scroller.scroll(function (e) {
    _swrapper.scrollTop = _scrollernative.scrollTop;
    _twrapper.scrollLeft = _scrollernative.scrollLeft;
});

我们可以尝试 here



我很欣赏您的代码可能会在页面上查看多个控件,这就是您使用类并需要查找S(this)而不是使用ID的原因。

为此,我仍然会使用上面的缓存方法,但在此之前我们需要逐个创建它们。 (在我们初始化每个实例的scroll()函数之前进行缓存)

(function() {
    var _scroller = $('.center-wrapper'),
    _swrapper = $('.sidebar-wrapper').get(0),
    _twrapper = $('.topbar-wrapper').get(0);

    function setScrollers(_thisscroller) {
        _scrollernative = _thisscroller.get(0);
        /* start the scroll listener per this event */
        _thisscroller.scroll(function (e) {
            _swrapper.scrollTop = _scrollernative.scrollTop;
            _twrapper.scrollLeft = _scrollernative.scrollLeft;
        }); 
    }

    _scroller.each(function() { setScrollers($(this)); });

})();

我们可以尝试 here


但正如我所看到的那样.sidebar-wrapper和.topbar-wrapper是唯一的,mabye .center-wrapper也是独一无二的。

那么如何最终使用id来为这些元素缩短所有内容。

var _scroller = document.getElementById('center-wrapper'),
    _swrapper = document.getElementById('sidebar-wrapper'),
    _twrapper = document.getElementById('topbar-wrapper');

    _scroller.onscroll = function() {
      _swrapper.scrollTop = _scroller.scrollTop;
      _twrapper.scrollLeft = _scroller.scrollLeft;
    }; 

我们可以尝试 here


答案 3 :(得分:1)

而不是使用marginTop & marginLeft来更改.sidebar & .topbar的余量,使用scrollTop & scrollLeft来获取溢出的div .sidebar-wrapper & .topbar-wrapper并查看它现在是否可以在 Safari 中使用:

Demo Fiddle

$('.center-wrapper').scroll(function(e) {
   $('.sidebar-wrapper').scrollTop($(this).scrollTop());
   $('.topbar-wrapper').scrollLeft($(this).scrollLeft());
});

Try this fiddle made to look best in safari and tested