Прилепленная боковая панель: придерживаться нижней части при прокрутке вниз, верхней при прокрутке вверх


94

Я уже некоторое время искал решение моей проблемы с липкой боковой панелью. У меня есть конкретное представление о том, как я хочу, чтобы это действовало; эффективно, я бы хотел, чтобы он оставался внизу, когда вы прокручиваете вниз, а затем, как только вы прокручиваете обратно вверх, я хотел бы, чтобы он держался вверх, плавным движением (без прыжков). Я не могу найти пример того, чего пытаюсь достичь, поэтому я создал изображение, которое, надеюсь, проиллюстрирует мысль более четко:

Прилепленная боковая панель: придерживаться нижней части при прокрутке вниз, верхней при прокрутке вверх

  1. Боковая панель находится под заголовком.
  2. При прокрутке вниз боковая панель остается на одном уровне с содержимым страницы, поэтому вы можете прокручивать как боковую панель, так и содержимое.
  3. Достигнув нижней части боковой панели, боковая панель прикрепляется к нижней части области просмотра (большинство плагинов позволяют придерживаться только верхней части, некоторые, которые позволяют придерживаться нижней части, не допускают обоих).
  4. Достигните нижней части, боковая панель находится над нижним колонтитулом.
  5. При прокрутке вверх боковая панель остается на одном уровне с содержимым, поэтому вы можете снова прокручивать содержимое и боковую панель.
  6. Достигнув верхней части боковой панели, боковая панель прикрепляется к верхней части области просмотра.
  7. Достигните вершины, и боковая панель вернется под заголовок.

Надеюсь, этой информации достаточно. Я создал jsfiddle для тестирования любых плагинов / скриптов, которые я сбросил для этого вопроса: http://jsfiddle.net/jslucas/yr9gV/2/ .

Ответы:


25

+1 к очень красивому и наглядному изображению.

Я знаю, что это старый вопрос, но я случайно нашел тот же вопрос, опубликованный вами на forum.jquery.com, и один ответ там (от @ tucker973) , предложил одну хорошую библиотеку для этого и хотел поделиться им здесь.

Это называется липкий набор от @leafo

Здесь у вас есть код очень простого примера, который я подготовил, и рабочая демонстрация, чтобы увидеть результат.

Конечно, все кредиты принадлежат создателю плагина, я сделал этот пример только для того, чтобы показать его здесь. Мне нужно добиться того же результата, что и вам, и нашел этот плагин очень полезным.


Я использую этот небольшой плагин, как вы предлагаете, но ширина изменяется после того, как он остается сверху. Как я могу сделать это без изменений?
vijayrana

Привет @gmo! Я ищу то же самое, но оно не работает (не застревает сверху при прокрутке вверх), когда полоса прокрутки длиннее, чем область просмотра ...
Игорь Ласло

13

Спасибо за отличную графику. Я тоже искал решение этой проблемы!

К сожалению, другой опубликованный здесь ответ не соответствует требованию № 5, которое предусматривает возможность плавной прокрутки назад по боковой панели.

Я создал скрипт, который реализует все требования: http://jsfiddle.net/bN4qu/5/

Основная логика, которую необходимо реализовать:

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

В скрипте я использую преобразование CSS3 для перемещения целевого элемента, поэтому он не будет работать, например, в IE <9. Впрочем, логика использования другого подхода вполне логична.

Кроме того, я изменил вашу скрипку, чтобы на липкой боковой панели был градиентный фон. Это помогает показать, что демонстрируется правильное поведение.

Надеюсь, это кому-нибудь пригодится!


2
Для всех, кто ищет ответ, этот от Трэвиса - самый безупречный из всех, что я нашел. Спасибо чувак.
Marcovega

Отличная попытка, она просто сработала, когда я вставил это, что больше, чем я мог бы сказать для других плагинов :) Производительность сильно пострадала, но я думаю, что это в значительной степени данность для любой неродной липкой реализации.
jClark

Это была отличная отправная точка! Я $.cssзаключил функцию в a requestAnimationFrameи добавил функцию уничтожения / отмены привязки для использования в современных интерфейсных фреймворках, таких как vue / react. После этого производительность абсолютно не проблема!
Christophe Marois,

@Cristophe Marois, не могли бы вы привести пример на jsfiddle?
DuArme 07

спасибо, но этот код не работает для небольшой боковой панели, которая короче области просмотра (высота области просмотра)
Сейед Аббас Сейеди

12

Вот пример того, как это реализовать:

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%;
}

Демо: http://jsfiddle.net/ryanmaxwell/25QaE/

Это работает, как ожидалось, во всех сценариях и хорошо поддерживается в IE.


см. этот ответ и объясните stackoverflow.com/questions/28428327/…
theinlwin

@Anoop Naik - это почти хорошо, что я ищу ... sticky-kit не работает для боковых панелей, которые длиннее, чем область просмотра, у вас работает. Однако я бы хотел противоположного: когда я прокручиваю вниз, он остается наверху, а при прокрутке вверх - снизу ... не могли бы вы помочь мне, пожалуйста, с этим маленьким изменением в скрипке?
Игорь Ласло

1
@IgorLaszlo конечно, дай мне немного времени, через какое-то время расскажу тебе ...
Anoop Naik

Это также объясняет мою проблему: «Когда элемент с position: sticky« застревает »и длиннее, чем область просмотра, вы можете увидеть его содержимое только после прокрутки вниз контейнера. Было бы здорово, если бы« застрявший »элемент прокручивался с документом и остановится, как только он достигнет его нижнего края. Если пользователь прокрутит назад, то же самое произойдет снова, но в обратном порядке. " - написано другим человеком с той же проблемой ( stackoverflow.com/questions/47618271/… )
Игорь Ласло

@Anoop Naik! Спасибо за ваши усилия, но позвольте мне, я нашел плагин Sticky jquery для решения моей проблемы: abouolia.github.io/sticky-sidebar Еще раз спасибо!
Игорь Ласло

0

Я искал то же самое. Очевидно, мне нужно было найти какие-то непонятные термины, чтобы найти аналогичный вопрос с изображением. Оказывается, это именно то, что я искал. Мне не удалось найти никаких плагинов, поэтому я решил сделать это сам. Надеюсь, кто-то это увидит и доработает.

Вот быстрый и грязный образец HTML, который я использую.

<div id="main">
    <div class="col-1">
    </div>
    <div class="col-2">
        <div class="side-wrapper">
            sidebar content
        </div>
    </div>
</div>

Вот созданный мной jQuery:

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();

    });
}

Некоторые примечания:

  • .rectbtfad - самый нижний элемент на моей боковой панели
  • Я использую высоту своего #masthead, потому что это липкий заголовок, поэтому он должен его компенсировать
  • Есть проверка на col-2 float, поскольку я использую адаптивный дизайн и не хочу, чтобы он активировался на небольших экранах.

Если бы кто-нибудь мог уточнить это немного, было бы здорово.


0
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");

Рабочий пример: https://jsfiddle.net/L7xoopst/6/


добавить немного пояснения?
HaveNoDisplayName

Если вы обновите высоту липкого предмета, у него
возникнут

0

В репозитории Wordpress есть относительно неизвестный плагин, известный как WP Sticky Sidebar. Плагин делает именно то, что вы хотели (липкая боковая панель: придерживаться нижней части при прокрутке вниз, вверху при прокрутке вверх) WP Sticky Sidebar Репозиторий Wordpress Ссылка: https://wordpress.org/plugins/mystickysidebar/


Спасибо за информацию! Работает отлично. Забавно, что графическая иллюстрация поведения такая же, как и для избранного изображения плагина :)
Оксана Романив
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.