88 votes

Événement à détecter lorsque position:sticky est déclenché

J'utilise le nouveau position: sticky ( info ) pour créer une liste de contenu semblable à celle d'iOS.

Il fonctionne bien et est de loin supérieur à la précédente alternative JavaScript ( exemple ) mais pour autant que je sache, aucun événement n'est déclenché, ce qui signifie que je ne peux rien faire lorsque la barre atteint le haut de la page, contrairement à la solution précédente.

J'aimerais ajouter une classe (par ex. stuck ) lorsqu'un élément avec position: sticky en haut de la page. Existe-t-il un moyen d'écouter cela avec JavaScript ? L'utilisation de jQuery est correcte.

2voto

Will Koehler Points 1002

Il n'existe actuellement aucune solution native. Voir Cibler les éléments position:sticky qui sont actuellement dans un état "bloqué". . Cependant, je dispose d'une solution CoffeeScript qui fonctionne à la fois avec le langage natif et le langage de programmation. position: sticky et avec les polyfills qui mettent en œuvre le comportement de l'adhésif.

Ajoutez la classe "sticky" aux éléments que vous souhaitez rendre collants :

.sticky {
  position: -webkit-sticky;
  position: -moz-sticky;
  position: -ms-sticky;
  position: -o-sticky;
  position: sticky;
  top: 0px;
  z-index: 1;
}

CoffeeScript surveille la position des éléments "collants" et ajoute la classe "stuck" lorsqu'ils sont dans l'état "collant" :

$ -> new StickyMonitor

class StickyMonitor

  SCROLL_ACTION_DELAY: 50

  constructor: ->
    $(window).scroll @scroll_handler if $('.sticky').length > 0

  scroll_handler: =>
    @scroll_timer ||= setTimeout(@scroll_handler_throttled, @SCROLL_ACTION_DELAY)

  scroll_handler_throttled: =>
    @scroll_timer = null
    @toggle_stuck_state_for_sticky_elements()

  toggle_stuck_state_for_sticky_elements: =>
    $('.sticky').each ->
      $(this).toggleClass('stuck', this.getBoundingClientRect().top - parseInt($(this).css('top')) <= 1)

NOTE : Ce code ne fonctionne que pour la position verticale de l'autocollant.

2voto

Dan T Points 121

J'ai trouvé cette solution qui fonctionne comme un charme et qui est assez petite :)

Aucun élément supplémentaire n'est nécessaire.

Il s'exécute cependant sur l'événement de défilement de la fenêtre, ce qui constitue un petit inconvénient.

apply_stickies()

window.addEventListener('scroll', function() {
    apply_stickies()
})

function apply_stickies() {
    var _$stickies = [].slice.call(document.querySelectorAll('.sticky'))
    _$stickies.forEach(function(_$sticky) {
        if (CSS.supports && CSS.supports('position', 'sticky')) {
            apply_sticky_class(_$sticky)
        }
    })
}

function apply_sticky_class(_$sticky) {
    var currentOffset = _$sticky.getBoundingClientRect().top
    var stickyOffset = parseInt(getComputedStyle(_$sticky).top.replace('px', ''))
    var isStuck = currentOffset <= stickyOffset

    _$sticky.classList.toggle('js-is-sticky', isStuck)
}

Note : Cette solution ne prend pas en compte les éléments qui ont une adhérence inférieure. Elle ne fonctionne que pour des éléments comme un en-tête collant. Elle peut probablement être adaptée pour prendre en compte l'adhérence du bas.

1voto

Davey Points 909

Je sais que cela fait un certain temps que la question a été posée, mais j'ai trouvé une bonne solution à ce problème. Le plugin stickybits utilise position: sticky lorsque cela est possible, et applique une classe à l'élément lorsqu'il est "bloqué". Je l'ai utilisé récemment avec de bons résultats, et, au moment où j'écris ces lignes, il est en cours de développement (ce qui est un plus pour moi) :)

0voto

Serge Liatko Points 1

J'utilise ce snippet dans mon thème pour ajouter .is-stuck à la classe .site-header lorsqu'il est en position de blocage :

// noinspection JSUnusedLocalSymbols
(function (document, window, undefined) {

    let windowScroll;

    /**
     *
     * @param element {HTMLElement|Window|Document}
     * @param event {string}
     * @param listener {function}
     * @returns {HTMLElement|Window|Document}
     */
    function addListener(element, event, listener) {
        if (element.addEventListener) {
            element.addEventListener(event, listener);
        } else {
            // noinspection JSUnresolvedVariable
            if (element.attachEvent) {
                element.attachEvent('on' + event, listener);
            } else {
                console.log('Failed to attach event.');
            }
        }
        return element;
    }

    /**
     * Checks if the element is in a sticky position.
     *
     * @param element {HTMLElement}
     * @returns {boolean}
     */
    function isSticky(element) {
        if ('sticky' !== getComputedStyle(element).position) {
            return false;
        }
        return (1 >= (element.getBoundingClientRect().top - parseInt(getComputedStyle(element).top)));
    }

    /**
     * Toggles is-stuck class if the element is in sticky position.
     *
     * @param element {HTMLElement}
     * @returns {HTMLElement}
     */
    function toggleSticky(element) {
        if (isSticky(element)) {
            element.classList.add('is-stuck');
        } else {
            element.classList.remove('is-stuck');
        }
        return element;
    }

    /**
     * Toggles stuck state for sticky header.
     */
    function toggleStickyHeader() {
        toggleSticky(document.querySelector('.site-header'));
    }

    /**
     * Listen to window scroll.
     */
    addListener(window, 'scroll', function () {
        clearTimeout(windowScroll);
        windowScroll = setTimeout(toggleStickyHeader, 50);
    });

    /**
     * Check if the header is not stuck already.
     */
    toggleStickyHeader();

})(document, window);

-1voto

Stephen R Points 391

L'excellente réponse de @vsync était presque ce dont j'avais besoin, sauf que je "uglifie" mon code via Grunt, et que Grunt requiert certains styles de code JavaScript plus anciens. Voici le script ajusté que j'ai utilisé à la place :

var stickyElm = document.getElementById('header');
var observer = new IntersectionObserver(function (_ref) {
    var e = _ref[0];
    return e.target.classList.toggle('isSticky', e.intersectionRatio < 1);
}, {
    threshold: [1]
});
observer.observe( stickyElm );

Le CSS de cette réponse est inchangé

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X