Od jakiegoś czasu szukałem rozwiązania mojego problemu z lepkim paskiem bocznym. Mam konkretny pomysł, jak chciałbym, żeby to działało; efektywnie chciałbym, aby przy przewijaniu w dół przyklejał się do dołu, a gdy tylko przewiniesz z powrotem w górę, chciałbym, aby przylegał do góry płynnym ruchem (bez przeskakiwania). Nie jestem w stanie znaleźć przykładu tego, co próbuję osiągnąć, dlatego stworzyłem obraz, który mam nadzieję lepiej zilustruje ten punkt:
- Pasek boczny znajduje się pod nagłówkiem.
- Podczas przewijania w dół pasek boczny pozostaje na poziomie zawartości strony, dzięki czemu można przewijać zarówno pasek boczny, jak i zawartość.
- Sięgnij do dołu paska bocznego, pasek boczny przykleja się do dołu widoku (większość wtyczek pozwala tylko na przyklejanie się do góry, niektóre, które pozwalają na trzymanie się do dołu, nie pozwalają na oba).
- Sięgnij na dół, pasek boczny znajduje się nad stopką.
- Podczas przewijania w górę pasek boczny pozostaje na poziomie zawartości, dzięki czemu można ponownie przewijać zawartość i pasek boczny.
- Osiągnij górę paska bocznego, pasek boczny przylega do górnej części widoku.
- Dotrzyj do góry, a pasek boczny znajduje się z powrotem pod nagłówkiem.
Mam nadzieję, że to wystarczająca informacja. Utworzyłem jsfiddle, aby przetestować wszelkie wtyczki / skrypty, które zresetowałem dla tego pytania: http://jsfiddle.net/jslucas/yr9gV/2/ .
Dzięki za świetną grafikę. Szukałem też rozwiązania tego wyzwania!
Niestety, inna zamieszczona tutaj odpowiedź nie dotyczy wymagania nr 5, który określa możliwość płynnego przewijania paska bocznego.
Stworzyłem skrzypce, które realizują wszystkie wymagania: http://jsfiddle.net/bN4qu/5/
Podstawowa logika, którą należy zaimplementować, to:
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
W skrzypcach używam transformacji CSS3 do przenoszenia elementu docelowego, więc nie zadziała np. W IE <9. Logika jest jednak słuszna dla zastosowania innego podejścia.
Zmodyfikowałem również twoje skrzypce, aby lepki pasek boczny miał gradientowe tło. Pomaga to pokazać, że przejawia się właściwe zachowanie.
Mam nadzieję, że komuś się to przyda!
źródło
$.css
funkcję w arequestAnimationFrame
i dodałem funkcję niszczenia / rozpinania do użytku w nowoczesnych frameworkach frontendowych, takich jak vue / react. Po tym wydajność absolutnie nie jest problemem!Oto przykład, jak to zaimplementować:
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/
Działa to zgodnie z oczekiwaniami we wszystkich scenariuszach i jest również dobrze obsługiwane w IE.
źródło
Szukałem dokładnie tego samego. Najwyraźniej musiałem poszukać niejasnych terminów, aby znaleźć podobne pytanie z grafiką. Okazuje się, że właśnie tego szukałem. Nie mogłem znaleźć żadnych wtyczek, więc postanowiłem zrobić to sam. Miejmy nadzieję, że ktoś to zobaczy i dopracuje.
Oto szybki i brudny przykładowy kod HTML, którego używam.
<div id="main"> <div class="col-1"> </div> <div class="col-2"> <div class="side-wrapper"> sidebar content </div> </div> </div>
Oto jQuery, które stworzyłem:
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(); }); }
Kilka uwag:
Byłoby wspaniale, gdyby ktoś mógł to nieco bardziej udoskonalić.
źródło
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");
Przykład roboczy: https://jsfiddle.net/L7xoopst/6/
źródło
W repozytorium Wordpress jest stosunkowo nieznana wtyczka, znana jako WP Sticky Sidebar. Wtyczka robi dokładnie to, co chciałeś (Lepki pasek boczny: trzymaj się dołu podczas przewijania w dół, do góry podczas przewijania w górę) WP Sticky Sidebar Wordpress Link do repozytorium: https://wordpress.org/plugins/mystickysidebar/
źródło
Przykładowy dwukierunkowy Sticky Sidebar.
Jeśli ktoś potrzebuje lekkiego rozwiązania nie opartego na jQuery, zapraszam do zapoznania się z tym kodem: Two-direction-Sticky-Sidebar na GitHubie .
//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>
źródło