¿Cómo me desplazo a un elemento dentro de un Div desbordado?

159

Tengo 20 elementos de lista dentro de un div que solo pueden mostrar 5 a la vez. ¿Cuál es una buena manera de desplazarse al elemento n. ° 10 y luego al elemento n. ° 20? Sé la altura de todos los artículos.

El scrollTocomplemento hace esto, pero su fuente no es muy fácil de entender sin realmente entrar en él. No quiero usar este complemento.

Digamos que tengo una función que toma 2 elementos $parentDiv, $innerListItemni $innerListItem.offset().toptampoco $innerListItem.positon().topme da la correcta para scrollTop $ parentDiv.

mkoryak
fuente
Eliminó mi respuesta debido a su actualización. Pero créanme, habiendo tenido esta misma actitud antes, hay muchas peculiaridades del navegador con posición y márgenes, etc. El complemento es una ruta mucho más simple y menos lenta, y solo 3k antes de GZip.
Nick Craver
1
Utilice el complemento scrollTo de todos modos. No es dificil. $("#container").scrollTo(target, duration, options). El objetivo es solo un #anchor. Hecho.
Ryan McGeary
Parece que debería haber conocimiento para hacer esto sin un complemento. mañana
voy a
55
@mkoryak: el conocimiento está ahí afuera, pero cuando se tienen en cuenta las peculiaridades del navegador, básicamente terminas con ... el complemento .scrollTo , ¿por qué reinventar la rueda todo el tiempo?
Nick Craver
1
En mi humilde opinión, probablemente terminará con el subconjunto exacto de lo que necesita de .scrollTo hoy . Pero dentro de unos meses, mientras que .scrollTo se ha actualizado a IE9, FF4 y Chrome 6, que tendrá que empezar de nuevo ... 3k que parece ser un precio muy bajo para evitar esto.
Wim

Respuestas:

236

El $innerListItem.position().topes realmente relativo al .scrollTop()de su primer antepasado posicionado. Entonces, la forma de calcular el $parentDiv.scrollTop()valor correcto es comenzar asegurándose de que $parentDivesté posicionado. Si aún no tiene un explícito position, use position: relative. Los elementos $innerListItemy todos sus antepasados ​​hasta no $parentDivnecesitan tener una posición explícita. Ahora puede desplazarse hasta $innerListItemcon:

// 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
fuente
152

Este es mi propio complemento (colocará el elemento en la parte superior de la lista. Especialmente para overflow-y : auto. ¡Puede que no funcione overflow-x!):

NOTA : elemes el selector HTML de un elemento al que se desplazará la página. Cualquier cosa apoyado por jQuery, como: #myid, div.myclass, $(jquery object), [objeto 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 no necesita que esté animado, use:

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

Cómo utilizar:

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

Nota: #innerItempuede estar en cualquier lugar dentro #overflow_div. Realmente no tiene que ser un niño directo.

Probado en Firefox (23) y Chrome (28).

Si desea desplazarse por toda la página, marque esta pregunta .

lepe
fuente
que es elem?? No lo explicas en absoluto.
vsync
Obtendrá un mejor millaje / no conflicto si cambia $ en el fn.scrollTo a jQuery en su lugar
Barry Carlyon
Gran falla con esto, no contiene el alcance: $(this).scrollTop($(this).scrollTop() - $(this).offset().top + $(elem, $(this)).offset().top);- saludos
Luke Snowden
1
Si desea dejar un poco de margen superior entre la página y el elemento (que puede verse mejor en algunos casos), restar, por ejemplo, 10px, como: $(this).scrollTop() - $(this).offset().top + $(elem).offset().top - 10.
lepe
Si tiene diferencias de posición entre Firefox y Chrome (u otros navegadores), verifique su código html (vea este informe de error )
lepe
34

He ajustado la respuesta de Glenn Moss para tener en cuenta el hecho de que el desbordamiento div podría no estar en la parte superior de la página.

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

Estaba usando esto en una aplicación de Google Maps con una plantilla receptiva. En resolución> 800 px, la lista estaba en el lado izquierdo del mapa. En la resolución <800, la lista estaba debajo del mapa.

KPheasey
fuente
6

Las respuestas anteriores posicionarán el elemento interno en la parte superior del elemento de desbordamiento, incluso si está a la vista dentro del elemento de desbordamiento. No quería eso, así que lo modifiqué para no cambiar la posición de desplazamiento si el elemento está a la vista.

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;
};
Miguel
fuente
Esto me ayudo !! Ver stackoverflow.com/questions/48283932/…
gene b.
1

Escribo estas 2 funciones para hacerme la vida más fácil:

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

Y úsalos:

scrollToTop($innerListItem, $parentDiv, 200);
// or
scrollToCenter($innerListItem, $parentDiv, 200);
trank
fuente
elem.offset().topestá calculando desde la parte superior de la pantalla. Quiero que calcule a partir del elemento padre. Cómo puedo hacer eso ?
Santosh
0

Después de jugar con él durante mucho tiempo, esto es lo que se me ocurrió:

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

y lo llamo así

$("#basketListGridHolder").scrollTo('tr[data-uid="' + basketID + '"]');
bruce reeves
fuente