跟随滚动的侧边栏,但如果比视口高,则滚动自身

时间:2010-12-02 21:29:22

标签: javascript jquery css

(嘿,来自长期潜伏者的第一篇文章:)

我已经构建了一个简单的侧边栏,可以将“absolute-to-fixed”技巧留在屏幕上,但是想要考虑侧边栏高于视口的帐户情况。

所以我提出了这个想法。一切都从上面开始:

  • 在页面加载时,侧边栏在起始位置绘制,距视口顶部一定距离。
  • 当用户滚动页面时,侧边栏会随内容移动
  • 如果侧边栏垂直适合视口,则会固定到顶部

但是这里变得更有活力:

  • 如果侧边栏比视口高,则继续以滚动内容,直到到达侧边栏的底部,并在那里修复。侧边栏的顶部滚动到视口顶部之外。

  • 当用户向页面顶部滚动时,侧边栏会随内容一起移动,直到到达侧边栏的顶部,然后它就会修复。侧边栏的底部滚动到视口底部之外。

通过这种方式,侧边栏像往常一样对滚动作出反应,同时保持足够接近以便在长页面上找到。

任何指向示例的指针,或jQuery友好的代码片段/指南都将非常感激。

9 个答案:

答案 0 :(得分:20)

我对项目有这个确切的想法,这是一个如何实现它的例子

http://jsfiddle.net/ryanmaxwell/25QaE/

答案 1 :(得分:3)

您希望实现的行为称为“词缀”。 Twitter的Bootstrap前端框架有一个javascript组件,可以做你想要的。请检查section about the affix component。事实上,在那里你还可以看到所需行为的演示,左侧边栏中的菜单被贴上了。

您可以为affix init调用的offset参数定义函数,因此可以动态确定何时固定/取消固定元素。要查看上下文中的示例,请检查上述链接页面的来源,特别是查找application.js,它包含您可以在左侧看到的词缀设置。在这里,您可以看到脱离上下文的初始化代码:

// side bar
$('.bs-docs-sidenav').affix({
  offset: {
    top: function () { return $window.width() <= 980 ? 290 : 210 }
  , bottom: 270
  }
})

在页面来源中检查docs.css样式表可能也是值得的,其中包含了附加元素的一些定位和样式。检查这可能会解决您的问题,或至少可以给您和想法。

答案 2 :(得分:1)

由于这些例子都不适合我,我这样做了,我喜欢分享:

<强> JS

$(function () {
    var element = $('.right-container');
    var originalY = element.offset().top;
    var lastOffset = $(document).scrollTop();
    var diffToBottom = element.height() - $(window).height();

    $(window).on('scroll', function (event) {
        var scrollTop = $(window).scrollTop();        
        var currentOffset = $(document).scrollTop();
        var isScrollingDown = currentOffset > lastOffset;

        if (scrollTop > (diffToBottom + element.height())) {
            if (!element.hasClass('relative')) {
                element.css('top', scrollTop - (originalY + diffToBottom) + 'px');
                element.addClass('relative');
            }

            if (isScrollingDown) {
                var newTop = scrollTop < lastOffset ? 0 : scrollTop - (originalY + diffToBottom);
            }
            else if (scrollTop < element.offset().top) {
                var newTop = scrollTop - (originalY);
            }

            element.stop(false, false).animate({
                top: newTop
            }, 300);
        }
        else {
            if (scrollTop < originalY) {
                element.removeClass('relative');
            }

            element.stop(false, false).animate({
                top: scrollTop - (originalY)
            }, 300);
        }

        lastOffset = currentOffset;
    });
});

<强> CSS

.relative {
    position: relative;
}

当元素击中底部时它向下滚动,当顶部被击中时向上滚动。

答案 3 :(得分:1)

你可以使用Sticky Sidebar https://abouolia.github.io/sticky-sidebar/#examples

您需要“可滚动粘性元素”行为 请参阅此处示例https://abouolia.github.io/sticky-sidebar/examples/scrollable-element.html

答案 4 :(得分:0)

这样的事可能有用。我没有测试过,但理论上它看起来不错。

$(function(){
    var standardPosition = $('whatYouWantToScroll').offset().top;
    // Cache the standard position of the scrolling element
    $(window).scroll(function() {
        if ($(this).scrollTop() < standardPosition) {
            // if scroll pos is higher than the top of the element
            $('whatYouWantToScroll').css({
                position: 'relative',
                top: '0px'
            });
        } else if ($(window).scrollTop() > $('somethingToReferenceOnTheBottom').offset().top-($('whatYouWantToScroll').height())) {
            // if scroll position is lower than the top offset of the bottom reference
            // + the element that is scrolling height
            $('whatYouWantToScroll').css({
                position: 'absolute',
                top: $('somethingToReferenceOnTheBottom').offset().top-($('whatYouWantToScroll').height())
            });
        } else {
            // otherwise we're somewhere inbetween, fixed scrolling.
            $('whatYouWantToScroll').css({
                position: 'fixed'
            });
        }
    });
});

答案 5 :(得分:0)

我需要这个网站的确切功能,这就是我提出的,它似乎工作得非常好......我知道这篇文章很老,但我认为有些人可能对此感兴趣; )

(function($){
    $(document).ready(function(){
    sidebar_offset=$('#header').height()+10;
    $(window).scroll(function(){
        var sidebar=$('#primary');
    if($(this).scrollTop()<sidebar_offset){
        var height=$(window).height()-(sidebar_offset-$(this).scrollTop());
        css={height:height,marginTop:0};
        sidebar[0].scrollTop=0;
    }else{
        css.height=$(this).height();
        if($(this).scrollTop()>sidebar_offset){
            if(this.lastTop>$(this).scrollTop()){
                sidebar[0].scrollTop=sidebar[0].scrollTop-(this.lastTop-$(this).scrollTop());
            }else{
                sidebar[0].scrollTop=sidebar[0].scrollTop+($(this).scrollTop()-this.lastTop);
            }

            css.marginTop=($(this).scrollTop()-sidebar_offset)+'px';
        }else{
            sidebar[0].scrollTop=0;
        }
     }
     sidebar.css(css);
     this.lastTop=$(this).scrollTop();
    });
})(jQuery);

答案 6 :(得分:0)

您还要求举例;我经常遇到的一个例子是Facebook右栏广告。它比视口高,滚过第一个或第二个广告,在第三个广告停止。我认为这是由第三个广告的位置而不是侧边栏的整体高度来定义的,但这是一个有效的例子。

http://facebook.com

screenhots: http://i.imgur.com/dM3OZ.jpg /页面顶部 http://i.imgur.com/SxEZO.jpg /进一步向下

答案 7 :(得分:0)

这是纯javascript中非常有效且通用的代码:

//aside is your sidebar selector
var aside = document.querySelector('aside');
//sticky sidebar on scrolling
var startScroll = aside.offsetTop;
var endScroll = window.innerHeight - aside.offsetHeight;
var currPos = window.scrollY;
document.addEventListener('scroll', () => {
    var asideTop = parseInt(aside.style.top.replace('px;', ''));
    if (window.scrollY < currPos) {
        //scroll up
        if (asideTop < startScroll) {
            aside.style.top = (asideTop + currPos - window.scrollY) + 'px';
        } else if (asideTop > startScroll && asideTop != startScroll) {
            aside.style.top = startScroll + 'px';
        }
    } else {
        //scroll down
        if (asideTop > endScroll) {
            aside.style.top = (asideTop + currPos - window.scrollY) + 'px';
        } else if (asideTop < (endScroll) && asideTop != endScroll) {
            aside.style.top = endScroll + 'px';
        }
    }
    currPos = window.scrollY;
});
//this code will bring to you sidebar on refresh page
function asideToMe() {
    setTimeout(() => {
        aside.style.top = startScroll + 'px';
    }, 300);
}
asideToMe();
body{
  padding: 0 20px;
}
#content {
  height: 1200px;
}
header {
  width: 100%;
  height: 150px;
  background: #aaa;
}
main {
  float: left;
  width: 65%;
  height: 100%;
  background: linear-gradient(180deg, rgba(255,255,255,1) 0%, rgba(0,0,0,1) 100%);
}
aside {
  float: right;
  width: 30%;
  height: 800px;
  position: sticky;
  top: 100px;
  background: linear-gradient(0deg, rgba(2,0,36,1) 0%, rgba(22,153,66,1) 51%, rgba(167,255,0,1) 100%);
}
footer {
  width: 100%;
  height: 70px;
  background: #555;
}
<body>
  <header>Header</header>
  <div id="content">
    <main>Content</main>
    <aside>Sidebar</aside>
  </div>
  <footer>Footer</footer>
</body>

答案 8 :(得分:0)

Juste在没有jQuery(香草JS)的情况下制作了@Ryan JsFiddle的叉子:

https://jsfiddle.net/Kalane/8w09orvg/6/

function getOffset(el) {
  const win = el.ownerDocument.defaultView
  const rect = el.getBoundingClientRect()
  return {
    top: rect.top + win.pageYOffset,
    left: rect.left + win.pageXOffset
  }
}

function getPosition(el) {
  const offset = this.getOffset(el)
  const parentOffset = this.getOffset(el.parentNode)
  return {
    top: offset.top - parentOffset.top,
    left: offset.left - parentOffset.left,
  }
}


let lastScrollTop = document.documentElement.scrollTop
let wasScrollingDown = true
const sidebar = document.querySelector('#sidebar')
const initialSidebarTop = getPosition(sidebar).top


window.addEventListener('scroll', () => {
  const windowHeight = window.innerHeight
  const sidebarHeight = Math.ceil(sidebar.getBoundingClientRect().height)

  const scrollTop = document.documentElement.scrollTop
  const scrollBottom = scrollTop + windowHeight

  const sidebarTop = getPosition(sidebar).top
  const sidebarBottom = sidebarTop + sidebarHeight

  const heightDelta = Math.abs(windowHeight - sidebarHeight)
  const scrollDelta = lastScrollTop - scrollTop

  const isScrollingDown = (scrollTop > lastScrollTop)
  const isWindowLarger = (windowHeight > sidebarHeight)

  if ((isWindowLarger && scrollTop > initialSidebarTop) || (!isWindowLarger && scrollTop > initialSidebarTop + heightDelta)) {
    sidebar.classList.add('fixed')
  } else if (!isScrollingDown && scrollTop <= initialSidebarTop) {
    sidebar.classList.remove('fixed')
  }

  const dragBottomDown = (sidebarBottom <= scrollBottom && isScrollingDown)
  const dragTopUp = (sidebarTop >= scrollTop && !isScrollingDown)

  if (dragBottomDown) {
    if (isWindowLarger) {
      sidebar.style.top = 0
    } else {
      sidebar.style.top = `${-heightDelta}px`
    }
  } else if (dragTopUp) {
    sidebar.style.top = 0
  } else if (sidebar.classList.contains('fixed')) {
    const currentTop = parseInt(sidebar.style.top, 10)

    const minTop = -heightDelta
    const scrolledTop = currentTop + scrollDelta

    const isPageAtBottom = scrollTop + windowHeight >= document.body.scrollHeight
    const newTop = (isPageAtBottom) ? minTop : scrolledTop

    sidebar.style.top = `${newTop}px`
  }

  lastScrollTop = scrollTop
  wasScrollingDown = isScrollingDown
})