Barra lateral adhesiva: adhiérase a la parte inferior cuando se desplaza hacia abajo, arriba cuando se desplaza hacia arriba


94

He estado buscando durante algún tiempo una solución a mi problema de barra lateral pegajosa. Tengo una idea específica de cómo me gustaría que actuara; efectivamente, me gustaría que se quedara en la parte inferior a medida que te desplazas hacia abajo, y luego, tan pronto como te desplaces hacia arriba, me gustaría que se quedara en la parte superior, con un movimiento fluido (sin saltos). No puedo encontrar un ejemplo de lo que estoy tratando de lograr, así que he creado una imagen que espero que ilustre el punto más claramente:

Barra lateral adhesiva: adhiérase a la parte inferior cuando se desplaza hacia abajo, arriba cuando se desplaza hacia arriba

  1. La barra lateral se encuentra debajo del encabezado.
  2. A medida que se desplaza hacia abajo, la barra lateral permanece nivelada con el contenido de la página para que pueda desplazarse por la barra lateral y el contenido.
  3. Llegue a la parte inferior de la barra lateral, la barra lateral se adhiere a la parte inferior de la ventana gráfica (la mayoría de los complementos solo permiten adherirse a la parte superior, algunos que permiten adherirse a la parte inferior no permiten ambas).
  4. Llega a la parte inferior, la barra lateral se encuentra sobre el pie de página.
  5. A medida que se desplaza hacia arriba, la barra lateral permanece nivelada con el contenido para que pueda desplazarse por el contenido y la barra lateral nuevamente.
  6. Llegue a la parte superior de la barra lateral, la barra lateral se adhiere a la parte superior de la ventana gráfica.
  7. Llega a la parte superior y la barra lateral se ubica debajo del encabezado.

Espero que esta sea suficiente información. He creado un jsfiddle para probar cualquier complemento / script, que he restablecido para esta pregunta: http://jsfiddle.net/jslucas/yr9gV/2/ .

Respuestas:


25

+1 a la imagen muy bonita e ilustrativa.

Sé que es una pregunta antigua, pero casualmente encontré la misma pregunta publicada por usted en forum.jquery.com y una respuesta allí (por @ tucker973) , sugerí una buena biblioteca para hacer esto y quería compartirla aquí.

Se llama sticky-kit por @leafo

Aquí tienes el código de un ejemplo muy básico que preparé y una demostración funcional para ver el resultado.

Por supuesto, todos los créditos van al creador del complemento, solo hice este ejemplo para mostrarlo aquí. Necesito lograr el mismo resultado que buscaste y encontré este complemento muy útil.


Utilizo este pequeño complemento como sugieres, pero el ancho cambia después de que se pega en la parte superior. ¿Cómo puedo hacerlo sin cambios?
vijayrana

¡Hola @gmo! Estoy buscando lo mismo, pero no funciona (no se pega en la parte superior al desplazarse hacia arriba) cuando la barra de desplazamiento es más larga que la ventana
Igor Laszlo

13

Gracias por el gran gráfico. ¡También estaba buscando una solución a este desafío!

Desafortunadamente, la otra respuesta publicada aquí no aborda el requisito n. ° 5 que estipula la capacidad de desplazarse hacia atrás por la barra lateral sin problemas.

Creé un violín que implementa todos los requisitos: http://jsfiddle.net/bN4qu/5/

La lógica central que debe implementarse es:

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

En el violín, uso la transformación CSS3 para mover el elemento de destino, por lo que no funcionará, por ejemplo, en IE <9. Sin embargo, la lógica es sólida para usar un enfoque diferente.

Además, modifiqué tu violín para que la barra lateral adhesiva tenga un fondo degradado. Esto ayuda a mostrar que se está exhibiendo el comportamiento adecuado.

¡Espero que esto sea útil para alguien!


2
Para cualquiera que busque una respuesta, esta de Travis es la más perfecta que he encontrado hasta ahora. Gracias hombre.
marcovega

Un gran intento, básicamente funcionó cuando dejé esto, que es más de lo que podría decir de otros complementos :) El rendimiento tuvo un gran impacto, pero creo que eso es prácticamente un hecho con cualquier implementación pegajosa no nativa.
jClark

¡Este fue un excelente punto de partida! Envolví la $.cssfunción en requestAnimationFrameay agregué una función de destrucción / desvinculación para su uso en marcos frontend modernos como vue / react. ¡El rendimiento no es un problema en absoluto después de eso!
Christophe Marois

@Cristophe Marois, ¿puede dar un ejemplo sobre jsfiddle, por favor?
DuArme

gracias, pero este código no funciona para la barra lateral pequeña que es más corta de la ventana
gráfica

12

Aquí hay un ejemplo de cómo implementar esto:

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

Demostración: http://jsfiddle.net/ryanmaxwell/25QaE/

Esto funciona como se esperaba en todos los escenarios y también es compatible con IE.


vea esta respuesta y explique stackoverflow.com/questions/28428327/…
theinlwin

@Anoop Naik: eso es casi bueno lo que estoy buscando ... sticky-kit no funciona para barras laterales que son más largas que la ventana gráfica, la tuya funciona. Sin embargo, me gustaría lo contrario: cuando me desplazo hacia abajo, se pega en la parte superior, y al desplazarse hacia arriba, se pega en la parte inferior ... ¿me pueden ayudar por favor con ese pequeño cambio en un violín por favor?
Igor Laszlo

1
@IgorLaszlo seguro, dame algo de tiempo, te actualizaré en algún momento ...
Anoop Naik

Esto también explica mi problema: "Cuando el elemento con position: sticky está" atascado "y es más largo que la ventana gráfica, solo puedes ver su contenido después de desplazarte hasta la parte inferior del contenedor. Sería genial si el elemento" atascado "se desplazara con el documento y se detuvo, una vez que llega a su borde inferior. Si el usuario se desplazara hacia atrás, volvería a suceder lo mismo, pero al revés ". - escrito por otra persona que tiene el mismo problema ( stackoverflow.com/questions/47618271/… )
Igor Laszlo

@Anoop Naik! Gracias por tu esfuerzo, pero déjalo, por favor, encontré el complemento Sticky jquery para resolver mi problema: abouolia.github.io/sticky-sidebar ¡ Gracias de nuevo!
Igor Laszlo

0

Estaba buscando exactamente lo mismo. Aparentemente, necesitaba buscar algunos términos oscuros solo para encontrar una pregunta similar con el gráfico. Resulta que es exactamente lo que estaba buscando. No pude encontrar ningún complemento, así que decidí hacerlo yo mismo. Con suerte, alguien verá esto y lo perfeccionará.

Aquí hay una muestra rápida y sucia de HTML que estoy usando.

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

Aquí está el jQuery que hice:

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

    });
}

Algunas notas:

  • .rectbtfad es el elemento más inferior en mi barra lateral
  • Estoy usando la altura de mi #masthead porque es un encabezado pegajoso, por lo que debe compensarlo
  • Hay una verificación para col-2 float ya que estoy usando un diseño receptivo y no quiero que esto se active en pantallas más pequeñas

Si alguien puede refinar esto un poco más, sería genial.


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

Ejemplo de trabajo: https://jsfiddle.net/L7xoopst/6/


agregar un poco de explicación?
HaveNoDisplayName

Si actualiza la altura dentro del elemento adhesivo, esto tiene algunos problemas
Callam

0

Hay un complemento relativamente desconocido en el repositorio de Wordpress conocido como WP Sticky Sidebar. El complemento hace exactamente lo que usted quería (barra lateral adhesiva: adhiérase a la parte inferior cuando se desplaza hacia abajo, arriba cuando se desplaza hacia arriba) WP Sticky Sidebar Wordpress Repository Link: https://wordpress.org/plugins/mystickysidebar/


Gracias por la info! Funcionó perfectamente. Es gracioso que el gráfico de ilustración de comportamiento sea el mismo para la imagen destacada del complemento :)
Oksana Romaniv
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.