Comment faire défiler jusqu'à un élément dans une Div débordée?

159

J'ai 20 éléments de liste à l'intérieur d'un div qui ne peuvent en afficher que 5 à la fois. Quelle est la bonne façon de faire défiler jusqu'à l'élément n ° 10, puis l'élément n ° 20? Je connais la hauteur de tous les objets.

Le scrollToplugin fait cela, mais sa source n'est pas super facile à comprendre sans vraiment y entrer. Je ne veux pas utiliser ce plugin.

Disons que j'ai une fonction qui prend 2 éléments $parentDiv, $innerListItemni $innerListItem.offset().topne $innerListItem.positon().topme donne le scrollTop correct pour $ parentDiv.

mkoryak
la source
J'ai supprimé ma réponse en raison de votre mise à jour. Mais croyez-moi, ayant eu cette même attitude avant, il y a beaucoup de bizarreries de navigateur avec la position et les marges, etc ... le plugin est une route beaucoup plus simple et moins longue, et seulement 3k avant GZip.
Nick Craver
1
Utilisez quand même le plugin scrollTo. Ce n'est pas difficile. $("#container").scrollTo(target, duration, options). La cible est juste un #anchor. Terminé.
Ryan McGeary
il semble qu'il devrait y avoir des connaissances pour faire cela sans plugin. demain malade commencer à lire la source
mkoryak
5
@mkoryak - Les connaissances sont là, mais lorsque vous tenez compte des bizarreries du navigateur, vous vous retrouvez essentiellement avec ... le plugin .scrollTo , alors pourquoi réinventer la roue tout le temps?
Nick Craver
1
IMHO, vous vous retrouverez probablement avec le sous-ensemble exact de ce dont vous avez besoin de .scrollTo aujourd'hui . Mais dans quelques mois, alors que .scrollTo a été mis à jour vers IE9, FF4 et Chrome 6, vous devrez recommencer ... 3k semble être un prix très bas pour éviter cela.
Wim

Réponses:

236

Le $innerListItem.position().topest en fait relatif au .scrollTop()de son premier ancêtre positionné. Donc, la façon de calculer la $parentDiv.scrollTop()valeur correcte est de commencer par s'assurer qu'elle $parentDivest positionnée. S'il n'a pas déjà d'explicite position, utilisez position: relative. Les éléments $innerListItemet tous ses ancêtres jusqu'à ne $parentDivdoivent avoir aucune position explicite. Vous pouvez maintenant faire défiler jusqu'à $innerListItemavec:

// Scroll to the top
$parentDiv.scrollTop($parentDiv.scrollTop() + $innerListItem.position().top);

// Scroll to the center
$parentDiv.scrollTop($parentDiv.scrollTop() + $innerListItem.position().top
    - $parentDiv.height()/2 + $innerListItem.height()/2);
Glenn Moss
la source
152

Ceci est mon propre plugin (positionnera l'élément en haut de la liste. Spécialement pour overflow-y : auto. Peut ne pas fonctionner avec overflow-x!):

REMARQUE : elemest le sélecteur HTML d'un élément vers lequel défilera la page. Tout ce que soutenu par jQuery, comme: #myid, div.myclass, $(jquery object), [objet dom], etc.

jQuery.fn.scrollTo = function(elem, speed) { 
    $(this).animate({
        scrollTop:  $(this).scrollTop() - $(this).offset().top + $(elem).offset().top 
    }, speed == undefined ? 1000 : speed); 
    return this; 
};

Si vous n'avez pas besoin de l'animer, utilisez:

jQuery.fn.scrollTo = function(elem) { 
    $(this).scrollTop($(this).scrollTop() - $(this).offset().top + $(elem).offset().top); 
    return this; 
};

Comment utiliser:

$("#overflow_div").scrollTo("#innerItem");
$("#overflow_div").scrollTo("#innerItem", 2000); //custom animation speed 

Remarque: #innerItempeut être n'importe où à l'intérieur #overflow_div. Ce n'est pas vraiment un enfant direct.

Testé dans Firefox (23) et Chrome (28).

Si vous souhaitez faire défiler toute la page, cochez cette question .

lépe
la source
qu'est ce que c'est elem?? vous ne l'expliquez pas du tout.
vsync
Vous obtenez un meilleur kilométrage / non-conflit si vous échangez $ dans le fn.scrollTo vers jQuery à la place
Barry Carlyon
Gros défaut avec ça, vous ne contenant pas la portée: $(this).scrollTop($(this).scrollTop() - $(this).offset().top + $(elem, $(this)).offset().top);- salutations
Luke Snowden
1
Si vous voulez laisser une certaine marge supérieure entre la page et l'élément (qui peut mieux dans certains cas), soustraire, par exemple, 10px, comme: $(this).scrollTop() - $(this).offset().top + $(elem).offset().top - 10.
lepe
Si vous rencontrez des différences de position entre Firefox et Chrome (ou d'autres navigateurs), vérifiez votre code html (voir ce rapport de bogue )
lepe
34

J'ai ajusté la réponse de Glenn Moss pour tenir compte du fait que la division de débordement pourrait ne pas être en haut de la page.

parentDiv.scrollTop(parentDiv.scrollTop() + (innerListItem.position().top - parentDiv.position().top) - (parentDiv.height()/2) + (innerListItem.height()/2)  )

J'utilisais ceci sur une application google maps avec un modèle réactif. Avec une résolution> 800px, la liste se trouvait sur le côté gauche de la carte. Pour une résolution <800, la liste était sous la carte.

KPheasey
la source
6

Les réponses ci-dessus positionneront l'élément interne en haut de l'élément de débordement même s'il est visible à l'intérieur de l'élément de débordement. Je ne voulais pas cela, alors je l'ai modifié pour ne pas changer la position de défilement si l'élément est en vue.

jQuery.fn.scrollTo = function(elem, speed) {
    var $this = jQuery(this);
    var $this_top = $this.offset().top;
    var $this_bottom = $this_top + $this.height();
    var $elem = jQuery(elem);
    var $elem_top = $elem.offset().top;
    var $elem_bottom = $elem_top + $elem.height();

    if ($elem_top > $this_top && $elem_bottom < $this_bottom) {
        // in view so don't do anything
        return;
    }
    var new_scroll_top;
    if ($elem_top < $this_top) {
        new_scroll_top = {scrollTop: $this.scrollTop() - $this_top + $elem_top};
    } else {
        new_scroll_top = {scrollTop: $elem_bottom - $this_bottom + $this.scrollTop()};
    }
    $this.animate(new_scroll_top, speed === undefined ? 100 : speed);
    return this;
};
Mike
la source
Cela m'a aidé !! Voir stackoverflow.com/questions/48283932/…
gène b.
1

J'écris ces 2 fonctions pour me faciliter la vie:

function scrollToTop(elem, parent, speed) {
    var scrollOffset = parent.scrollTop() + elem.offset().top;
    parent.animate({scrollTop:scrollOffset}, speed);
    // parent.scrollTop(scrollOffset, speed);
}

function scrollToCenter(elem, parent, speed) {
    var elOffset = elem.offset().top;
    var elHeight = elem.height();
    var parentViewTop = parent.offset().top;
    var parentHeight = parent.innerHeight();
    var offset;

    if (elHeight >= parentHeight) {
        offset = elOffset;
    } else {
        margin = (parentHeight - elHeight)/2;
        offset = elOffset - margin;
    }

    var scrollOffset = parent.scrollTop() + offset - parentViewTop;

    parent.animate({scrollTop:scrollOffset}, speed);
    // parent.scrollTop(scrollOffset, speed);
}

Et utilisez-les:

scrollToTop($innerListItem, $parentDiv, 200);
// or
scrollToCenter($innerListItem, $parentDiv, 200);
trank
la source
elem.offset().topcalcule à partir du haut de l'écran. Je veux que calculer à partir de l'élément parent. Comment je fais ça ?
Santosh
0

Après avoir joué avec pendant très longtemps, voici ce que j'ai trouvé:

    jQuery.fn.scrollTo = function (elem) {
        var b = $(elem);
        this.scrollTop(b.position().top + b.height() - this.height());
    };

et je l'appelle comme ça

$("#basketListGridHolder").scrollTo('tr[data-uid="' + basketID + '"]');
Bruce Reeves
la source