Já faz algum tempo que procuro uma solução para o meu problema de barra lateral pegajosa. Tenho uma ideia específica de como gostaria que ele atuasse; efetivamente, gostaria que ficasse na parte inferior à medida que você rola para baixo e, assim que você rolar de volta para cima, gostaria que ficasse no topo, em um movimento fluido (sem pulos). Não consigo encontrar um exemplo do que estou tentando alcançar, então criei uma imagem que espero ilustrar o ponto de forma mais clara:
- A barra lateral fica sob o cabeçalho.
- Conforme você rola para baixo, a barra lateral permanece nivelada com o conteúdo da página para que você possa rolar tanto pela barra lateral quanto pelo conteúdo.
- Alcance a parte inferior da barra lateral, a barra lateral adere à parte inferior da janela de visualização (a maioria dos plug-ins permite apenas a fixação no topo, alguns que permitem a fixação na parte inferior não permitem os dois).
- Chegue ao fundo, a barra lateral fica acima do rodapé.
- Conforme você rola para cima, a barra lateral permanece nivelada com o conteúdo para que você possa rolar pelo conteúdo e pela barra lateral novamente.
- Chegue ao topo da barra lateral, a barra lateral adere ao topo da janela de visualização.
- Alcance o topo e a barra lateral ficará abaixo do cabeçalho.
Espero que seja informação suficiente. Eu criei um jsfiddle para testar quaisquer plug-ins / scripts, que eu redefini para esta questão: http://jsfiddle.net/jslucas/yr9gV/2/ .
Obrigado pelo ótimo gráfico. Também procurava uma solução para este desafio!
Infelizmente, a outra resposta postada aqui não atende ao requisito nº 5, que estipula a capacidade de rolar de volta pela barra lateral sem problemas.
Criei um violino que implementa todos os requisitos: http://jsfiddle.net/bN4qu/5/
A lógica central que precisa ser implementada é:
If scrolling up OR the element is shorter than viewport Then Set top of element to top of viewport If scrolled above top of element If scrolling down then Set bottom of element at bottom of viewport If scrolled past bottom of element
No violino, eu uso a transformação CSS3 para mover o elemento de destino, por isso não funcionará, por exemplo, no IE <9. A lógica é válida para usar uma abordagem diferente.
Além disso, modifiquei seu violino para que a barra lateral aderente tenha um fundo gradiente. Isso ajuda a mostrar que o comportamento adequado está sendo exibido.
Espero que isso seja útil para alguém!
fonte
$.css
função em arequestAnimationFrame
e adicionei uma função destroy / unbind para uso em frameworks front-end modernos como vue / react. O desempenho não é um problema depois disso!Aqui está um exemplo de como implementar isso:
JavaScript:
$(function() { var $window = $(window); var lastScrollTop = $window.scrollTop(); var wasScrollingDown = true; var $sidebar = $("#sidebar"); if ($sidebar.length > 0) { var initialSidebarTop = $sidebar.position().top; $window.scroll(function(event) { var windowHeight = $window.height(); var sidebarHeight = $sidebar.outerHeight(); var scrollTop = $window.scrollTop(); var scrollBottom = scrollTop + windowHeight; var sidebarTop = $sidebar.position().top; var sidebarBottom = sidebarTop + sidebarHeight; var heightDelta = Math.abs(windowHeight - sidebarHeight); var scrollDelta = lastScrollTop - scrollTop; var isScrollingDown = (scrollTop > lastScrollTop); var isWindowLarger = (windowHeight > sidebarHeight); if ((isWindowLarger && scrollTop > initialSidebarTop) || (!isWindowLarger && scrollTop > initialSidebarTop + heightDelta)) { $sidebar.addClass('fixed'); } else if (!isScrollingDown && scrollTop <= initialSidebarTop) { $sidebar.removeClass('fixed'); } var dragBottomDown = (sidebarBottom <= scrollBottom && isScrollingDown); var dragTopUp = (sidebarTop >= scrollTop && !isScrollingDown); if (dragBottomDown) { if (isWindowLarger) { $sidebar.css('top', 0); } else { $sidebar.css('top', -heightDelta); } } else if (dragTopUp) { $sidebar.css('top', 0); } else if ($sidebar.hasClass('fixed')) { var currentTop = parseInt($sidebar.css('top'), 10); var minTop = -heightDelta; var scrolledTop = currentTop + scrollDelta; var isPageAtBottom = (scrollTop + windowHeight >= $(document).height()); var newTop = (isPageAtBottom) ? minTop : scrolledTop; $sidebar.css('top', newTop); } lastScrollTop = scrollTop; wasScrollingDown = isScrollingDown; }); } });
CSS:
#sidebar { width: 180px; padding: 10px; background: red; float: right; } .fixed { position: fixed; right: 50%; margin-right: -50%; }
Demo: http://jsfiddle.net/ryanmaxwell/25QaE/
Isso funciona conforme o esperado em todos os cenários e também é bem suportado no IE.
fonte
Eu estava procurando exatamente a mesma coisa. Aparentemente, precisei pesquisar alguns termos obscuros apenas para encontrar uma pergunta semelhante com o gráfico. Acontece que é exatamente o que eu estava procurando. Não consegui encontrar nenhum plug-in, então decidi fazer sozinho. Espero que alguém veja isso e o refine.
Aqui está um exemplo rápido de html que estou usando.
<div id="main"> <div class="col-1"> </div> <div class="col-2"> <div class="side-wrapper"> sidebar content </div> </div> </div>
Aqui está o jQuery que fiz:
var lastScrollPos = $(window).scrollTop(); var originalPos = $('.side-wrapper').offset().top; if ($('.col-2').css('float') != 'none') { $(window).scroll(function(){ var rectbtfadPos = $('.rectbtfad').offset().top + $('.rectbtfad').height(); // scroll up direction if ( lastScrollPos > $(window).scrollTop() ) { // unstick if scrolling the opposite direction so content will scroll with user if ($('.side-wrapper').css('position') == 'fixed') { $('.side-wrapper').css({ 'position': 'absolute', 'top': $('.side-wrapper').offset().top + 'px', 'bottom': 'auto' }); } // if has reached the original position, return to relative positioning if ( ($(window).scrollTop() + $('#masthead').height()) < originalPos ) { $('.side-wrapper').css({ 'position': 'relative', 'top': 'auto', 'bottom': 'auto' }); } // sticky to top if scroll past top of sidebar else if ( ($(window).scrollTop() + $('#masthead').height()) < $('.side-wrapper').offset().top && $('.side-wrapper').css('position') == 'absolute' ) { $('.side-wrapper').css({ 'position': 'fixed', 'top': 15 + $('#masthead').height() + 'px', // padding to compensate for sticky header 'bottom': 'auto' }); } } // scroll down else { // unstick if scrolling the opposite direction so content will scroll with user if ($('.side-wrapper').css('position') == 'fixed') { $('.side-wrapper').css({ 'position': 'absolute', 'top': $('.side-wrapper').offset().top + 'px', 'bottom': 'auto' }); } // check if rectbtfad (bottom most element) has reached the bottom if ( ($(window).scrollTop() + $(window).height()) > rectbtfadPos && $('.side-wrapper').css('position') != 'fixed' ) { $('.side-wrapper').css({ 'width': $('.col-2').width(), 'position': 'fixed', 'bottom': '0', 'top': 'auto' }); } } // set last scroll position to determine if scrolling up or down lastScrollPos = $(window).scrollTop(); }); }
Algumas notas:
Se alguém puder refinar isso um pouco mais, seria ótimo.
fonte
function fixMe(id) { var e = $(id); var lastScrollTop = 0; var firstOffset = e.offset().top; var lastA = e.offset().top; var isFixed = false; $(window).scroll(function(event){ if (isFixed) { return; } var a = e.offset().top; var b = e.height(); var c = $(window).height(); var d = $(window).scrollTop(); if (b <= c - a) { e.css({position: "fixed"}); isFixed = true; return; } if (d > lastScrollTop){ // scroll down if (e.css("position") != "fixed" && c + d >= a + b) { e.css({position: "fixed", bottom: 0, top: "auto"}); } if (a - d >= firstOffset) { e.css({position: "absolute", bottom: "auto", top: lastA}); } } else { // scroll up if (a - d >= firstOffset) { if (e.css("position") != "fixed") { e.css({position: "fixed", bottom: "auto", top: firstOffset}); } } else { if (e.css("position") != "absolute") { e.css({position: "absolute", bottom: "auto", top: lastA}); } } } lastScrollTop = d; lastA = a; }); } fixMe("#stick");
Exemplo de trabalho: https://jsfiddle.net/L7xoopst/6/
fonte
Existe um plugin relativamente desconhecido no repositório do Wordpress conhecido como WP Sticky Sidebar. O plugin faz exatamente o que você queria (barra lateral aderente: ficar na parte inferior ao rolar para baixo, superior ao rolar para cima) WP Sticky Sidebar Repositório Wordpress Link: https://wordpress.org/plugins/mystickysidebar/
fonte
Amostra da barra lateral aderente de duas direções.
Se alguém precisa de uma solução leve não baseada em jQuery, convido você a se familiarizar com este código: Two-direction-Sticky-Sidebar no GitHub .
//aside selector const aside = document.querySelector('[data-sticky="true"]'), //varibles startScroll = 0; var endScroll = window.innerHeight - aside.offsetHeight -500, currPos = window.scrollY; screenHeight = window.innerHeight, asideHeight = aside.offsetHeight; aside.style.top = startScroll + 'px'; //check height screen and aside on resize window.addEventListener('resize', ()=>{ screenHeight = window.innerHeight; asideHeight = aside.offsetHeight; }); //main function document.addEventListener('scroll', () => { endScroll = window.innerHeight - aside.offsetHeight; let asideTop = parseInt(aside.style.top.replace('px;', '')); if(asideHeight>screenHeight){ 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; }, { capture: true, passive: true });
body{ padding: 0 20px; } #content { height: 2000px; } header { width: 100%; height: 150px; background: #aaa; } main { float: left; width: 65%; height: 100%; background: #444; } aside { float: right; width: 30%; position: sticky; top: 0px; background: #777; } li { height: 50px; } footer { width: 100%; height: 300px; background: #555; position: relative; bottom: 0; }
<!DOCTYPE html> <head> <link href="/src/style.css" rel="preload" as="style"/> </head> <body> <header>Header</header> <div id="content"> <main>Content</main> <aside data-sticky="true"> <lu> <li>Top</li> <li>sidebar</li> <li>sidebar</li> <li>sidebar</li> <li>sidebar</li> <li>sidebar</li> <li>sidebar</li> <li>sidebar</li> <li>sidebar</li> <li>sidebar</li> <li>sidebar</li> <li>sidebar</li> <li>sidebar</li> <li>sidebar</li> <li>sidebar</li> <li>sidebar</li> <li>Bottom</li> </lu> </aside> </div> <footer>Footer</footer> <script src='/src/script.js' async></script> </body> </html>
fonte