Comment faire défiler un élément dans la vue à l'aide de jQuery?

169

J'ai un document HTML avec des images dans un format de grille en utilisant <ul><li><img.... La fenêtre du navigateur a un défilement vertical et horizontal.

Question: Lorsque je clique sur une image <img>, comment puis-je faire défiler l'ensemble du document jusqu'à une position où se trouve l'image sur laquelle je viens de cliquer top:20px; left:20px?

J'ai parcouru ici des articles similaires ... même si je suis assez nouveau dans JavaScript et que je veux comprendre comment cela est réalisé pour moi-même.

Nasir
la source

Réponses:

192

Puisque vous voulez savoir comment cela fonctionne, je vais vous l'expliquer étape par étape.

Vous voulez d'abord lier une fonction en tant que gestionnaire de clics de l'image:

$('#someImage').click(function () {
    // Code to do scrolling happens here
});

Cela appliquera le gestionnaire de clics à une image avec id="someImage". Si vous souhaitez effectuer cette opération sur toutes les images, remplacez-les '#someImage'par 'img'.

Maintenant, pour le code de défilement réel:

  1. Obtenez les décalages d'image (par rapport au document):

    var offset = $(this).offset(); // Contains .top and .left
  2. Soustrayez 20 de topet left:

    offset.left -= 20;
    offset.top -= 20;
  3. Maintenant, animez les propriétés CSS scroll-top et scroll-left de <body>et <html>:

    $('html, body').animate({
        scrollTop: offset.top,
        scrollLeft: offset.left
    });
David Tang
la source
3
Cela ne fonctionne que pour les mises en page simples. Elle ne gère pas tous les cas comme le fait la .scrollIntoViewméthode W3C lorsque des éléments sont imbriqués dans plusieurs conteneurs débordants.
natevw
2
pourquoi réinventer la roue? Element.scrollIntoView(): developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
Serge
@Serge parce que c'est un brouillon de travail qui n'a pas été implémenté dans tous les navigateurs
John Smith
@JohnSmith s'il vous plaît lire attentivement le lien que j'ai posté, la section "Compatibilité du navigateur", vous verrez qu'il n'y a pas de navigateur ne supporte pas lescrollIntoView
Serge
@Serge Désolé à ce sujet, je ne savais pas qu'il y avait aussi scrollIntoViewOption (qui est celui qui n'est pas suffisamment pris en charge). Je suppose que c'est assez bon.
John Smith
377

Il existe une méthode DOM appelée scrollIntoView, qui est prise en charge par tous les principaux navigateurs, qui alignera un élément avec le haut / gauche de la fenêtre (ou aussi près que possible).

$("#myImage")[0].scrollIntoView();

Sur les navigateurs pris en charge, vous pouvez fournir des options:

$("#myImage")[0].scrollIntoView({
    behavior: "smooth", // or "auto" or "instant"
    block: "start" // or "end"
});

Alternativement, si tous les éléments ont des ID uniques, vous pouvez simplement modifier la hashpropriété de l' locationobjet pour la prise en charge des boutons Précédent / Suivant:

$(document).delegate("img", function (e) {
    if (e.target.id)
        window.location.hash = e.target.id;
});

Après cela, ajustez simplement les scrollTop/ scrollLeftproperties de -20:

document.body.scrollLeft -= 20;
document.body.scrollTop -= 20;
Andy E
la source
2
Je vous remercie! 5 autres articles SO et vous l'avez parfaitement cloué. Mon cas d'utilisation était un conteneur dans une boîte de dialogue d'amorçage qui doit faire défiler la vue.
Stephen Patten
10
A noter: $("#myImage")[0].scrollIntoView(false);alignera l'élément en bas de la fenêtre.
Philipp
1
Attention: le comportement «fluide» n'est pas largement pris en charge et scrollIntoViewpeut être une expérience déroutante pour les utilisateurs sans une sorte d'animation.
Cody Duval
1
@JohnHenckel: c'est incorrect, même IE 5.5 avait scrollIntoViewavec le paramètre booléen. J'essaierai d'envoyer une pull request pour caniuse.com plus tard.
Andy E
1
Remarque: avec Chrome, je ne pouvais pas faire défiler les <td>éléments dans la vue , je devais plutôt faire défiler leur parent ( <tr>) et cela fonctionnait.
CrazyTim
13

La solution la plus simple que j'ai vue

var offset = $("#target-element").offset();
$('html, body').animate({
    scrollTop: offset.top,
    scrollLeft: offset.left
}, 1000);

Tutoriel ici

Matt Watson
la source
Au risque de rendre les choses moins simples, j'ai également modifié ceci pour prendre en charge le défilement horizontal.
jpaugh
7

Il existe des méthodes pour faire défiler l'élément directement dans la vue, mais si vous souhaitez faire défiler jusqu'à un point relatif à partir d'un élément, vous devez le faire manuellement:

Dans le gestionnaire de clics, obtenez la position de l'élément par rapport au document, soustrayez 20et utilisez window.scrollTo:

var pos = $(this).offset();
var top = pos.top - 20;
var left = pos.left - 20;
window.scrollTo((left < 0 ? 0 : left), (top < 0 ? 0 : top));
Félix Kling
la source
7

Jetez un œil au plugin jQuery.scrollTo . Voici une démo .

Ce plugin a beaucoup d'options qui vont au-delà de ce que vous offre scrollIntoView natif . Par exemple, vous pouvez définir le défilement pour qu'il soit lisse, puis définir un rappel pour la fin du défilement.

Vous pouvez également jeter un œil à tous les plugins JQuery étiquetés "scroll" .

Rob Stevenson-Leggett
la source
J'obtenais différents décalages / positions dans chaque navigateur dans mon cas, et ce plugin m'a aidé à le réparer pour moi! Merci!
LaurelB
4

Voici un plugin jQuery rapide pour mapper joliment la fonctionnalité de navigateur intégrée:

$.fn.ensureVisible = function () { $(this).each(function () { $(this)[0].scrollIntoView(); }); };

...

$('.my-elements').ensureVisible();
Rob
la source
3

Après essais et erreurs, j'ai proposé cette fonction, qui fonctionne également avec iframe.

function bringElIntoView(el) {    
    var elOffset = el.offset();
    var $window = $(window);
    var windowScrollBottom = $window.scrollTop() + $window.height();
    var scrollToPos = -1;
    if (elOffset.top < $window.scrollTop()) // element is hidden in the top
        scrollToPos = elOffset.top;
    else if (elOffset.top + el.height() > windowScrollBottom) // element is hidden in the bottom
        scrollToPos = $window.scrollTop() + (elOffset.top + el.height() - windowScrollBottom);
    if (scrollToPos !== -1)
        $('html, body').animate({ scrollTop: scrollToPos });
}
Ton Škoda
la source
2

Juste un conseil. Fonctionne uniquement sur Firefox

Element.scrollIntoView ();

étudiant WP
la source
4
Cela fonctionne pour tous les navigateurs avec un paramètre booléen ou aucun paramètre du tout. Seul le paramètre d'objet plus avancé est moins pris en charge.
Risord
selon developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView, il y a un manque général de support du navigateur pour cela
lfender6445
Il est maintenant assez bien pris en charge uniquement Opera Mini: caniuse.com/#search=scrollIntoView
John Magnolia
Il existe un certain nombre de polyfills très légers pour cela.
Martin James
1

Mon interface utilisateur a une liste de vignettes à défilement vertical dans une barre de vignettes. Le but était de placer le pouce actuel au centre de la barre de vignettes. Je suis parti de la réponse approuvée, mais j'ai constaté qu'il y avait quelques ajustements pour vraiment centrer le pouce actuel. J'espère que ceci aide quelqu'un d'autre.

balisage:

<ul id='thumbbar'>
    <li id='thumbbar-123'></li>
    <li id='thumbbar-124'></li>
    <li id='thumbbar-125'></li>
</ul>

jquery:

// scroll the current thumb bar thumb into view
heightbar   = $('#thumbbar').height();
heightthumb = $('#thumbbar-' + pageid).height();
offsetbar   = $('#thumbbar').scrollTop();


$('#thumbbar').animate({
    scrollTop: offsetthumb.top - heightbar / 2 - offsetbar - 20
});
xeo
la source
0

2 étapes simples pour faire défiler vers le bas ou la fin.

Étape 1: obtenez la pleine hauteur de la div (conversation) défilante.

Étape 2: appliquez scrollTop sur ce div scrollable (conversation) en utilisant la valeur obtenue à l'étape 1.

var fullHeight = $('#conversation')[0].scrollHeight;

$('#conversation').scrollTop(fullHeight);

Les étapes ci-dessus doivent être appliquées pour chaque ajout sur la division de conversation.

Shiva Kumar P
la source
0

Après avoir essayé de trouver une solution qui gère toutes les circonstances (options pour animer le défilement, remplissage autour de l'objet une fois qu'il défile dans la vue, fonctionne même dans des circonstances obscures comme dans une iframe), j'ai finalement écrit ma propre solution à cela. Comme cela semble fonctionner lorsque de nombreuses autres solutions ont échoué, j'ai pensé le partager:

function scrollIntoViewIfNeeded($target, options) {

    var options = options ? options : {},
    $win = $($target[0].ownerDocument.defaultView), //get the window object of the $target, don't use "window" because the element could possibly be in a different iframe than the one calling the function
    $container = options.$container ? options.$container : $win,        
    padding = options.padding ? options.padding : 20,
    elemTop = $target.offset().top,
    elemHeight = $target.outerHeight(),
    containerTop = $container.scrollTop(),
    //Everything past this point is used only to get the container's visible height, which is needed to do this accurately
    containerHeight = $container.outerHeight(),
    winTop = $win.scrollTop(),
    winBot = winTop + $win.height(),
    containerVisibleTop = containerTop < winTop ? winTop : containerTop,
    containerVisibleBottom = containerTop + containerHeight > winBot ? winBot : containerTop + containerHeight,
    containerVisibleHeight = containerVisibleBottom - containerVisibleTop;

    if (elemTop < containerTop) {
        //scroll up
        if (options.instant) {
            $container.scrollTop(elemTop - padding);
        } else {
            $container.animate({scrollTop: elemTop - padding}, options.animationOptions);
        }
    } else if (elemTop + elemHeight > containerTop + containerVisibleHeight) {
        //scroll down
        if (options.instant) {
            $container.scrollTop(elemTop + elemHeight - containerVisibleHeight + padding);
        } else {
            $container.animate({scrollTop: elemTop + elemHeight - containerVisibleHeight + padding}, options.animationOptions);
        }
    }
}

$target est un objet jQuery contenant l'objet que vous souhaitez afficher si nécessaire.

options (facultatif) peut contenir les options suivantes passées dans un objet:

options.$container- un objet jQuery pointant vers l'élément contenant de $ target (en d'autres termes, l'élément dans le dom avec les barres de défilement). Par défaut, la fenêtre contenant l'élément $ target est suffisamment intelligente pour sélectionner une fenêtre iframe. N'oubliez pas d'inclure le $ dans le nom de la propriété.

options.padding- le remplissage en pixels à ajouter au-dessus ou au-dessous de l'objet lorsqu'il défile dans la vue. De cette façon, il n'est pas juste contre le bord de la fenêtre. La valeur par défaut est 20.

options.instant- si défini sur true, jQuery animate ne sera pas utilisé et le défilement apparaîtra instantanément à l'emplacement correct. La valeur par défaut est false.

options.animationOptions- toutes les options jQuery que vous souhaitez passer à la fonction d'animation jQuery (voir http://api.jquery.com/animate/ ). Avec cela, vous pouvez modifier la durée de l'animation ou faire exécuter une fonction de rappel lorsque le défilement est terminé. Cela ne fonctionne que si options.instantest défini sur false. Si vous avez besoin d'une animation instantanée mais avec un rappel, définissez options.animationOptions.duration = 0au lieu d'utiliser options.instant = true.

Dallas
la source