Como rolar para um elemento dentro de uma Div excedida?

159

Eu tenho 20 itens de lista dentro de uma div que só podem mostrar 5 por vez. Qual é uma boa maneira de rolar para o item 10 e depois para o item 20? Eu sei a altura de todos os itens.

O scrollToplugin faz isso, mas sua fonte não é super fácil de entender sem realmente entrar nele. Não quero usar este plugin.

Vamos dizer que eu tenho uma função que leva 2 elementos $parentDiv, $innerListItemnem $innerListItem.offset().topnem $innerListItem.positon().topme dá a scrollTop correta por US $ parentDiv.

mkoryak
fonte
Removida minha resposta devido à sua atualização. Mas confie em mim, tendo essa mesma atitude antes, existem muitas peculiaridades do navegador com posição e margens, etc ... o plug-in é uma rota muito mais simples e consome menos tempo, e apenas 3k antes do GZip.
Nick Craver
1
Use o plug-in scrollTo de qualquer maneira. Não é difícil. $("#container").scrollTo(target, duration, options). O alvo é apenas um #anchor. Feito.
Ryan McGeary
parece que deve haver conhecimento para fazer isso sem um plugin. amanhã doente começar a ler a fonte
mkoryak
5
@mkoryak - O conhecimento existe, mas quando você considera as peculiaridades do navegador, basicamente acaba com ... o plug-in .scrollTo , então por que reinventar a roda o tempo todo?
Nick Craver
1
IMHO, você provavelmente vai acabar com o subconjunto exato do que você precisa sair do .scrollTo hoje . Mas dentro de alguns meses, enquanto o .scrollTo foi atualizado para o IE9, FF4 e Chrome 6, você terá que começar de novo ... O 3k parece ser um preço muito baixo para evitar isso.
Wim

Respostas:

236

Na $innerListItem.position().topverdade, é relativo ao .scrollTop()primeiro ancestral posicionado. Portanto, a maneira de calcular o $parentDiv.scrollTop()valor correto é começar certificando-se de que $parentDivestá posicionado. Se ainda não tiver um explícito position, use position: relative. Os elementos $innerListIteme todos os seus ancestrais $parentDivprecisam ter nenhuma posição explícita. Agora você pode rolar para o $innerListItemcom:

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

Este é o meu próprio plugin (posicionará o elemento no topo da lista. Especialmente para overflow-y : auto. Pode não funcionar com overflow-x!):

NOTA : elemé o seletor HTML de um elemento para o qual a página será rolada. Qualquer coisa suportado pelo jQuery, como: #myid, div.myclass, $(jquery object), [objecto 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; 
};

Se você não precisar que ele seja animado, use:

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

Como usar:

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

Nota: #innerItempode estar em qualquer lugar dentro #overflow_div. Realmente não precisa ser uma criança direta.

Testado no Firefox (23) e Chrome (28).

Se você quiser rolar a página inteira, verifique esta pergunta .

lepe
fonte
o que é elem?? você não explica nada.
vsync
Você obter uma melhor quilometragem não conflito / se você trocar $ no fn.scrollTo para jQuery vez
Barry Carlyon
Grande falha com isso, você não contém o escopo: $(this).scrollTop($(this).scrollTop() - $(this).offset().top + $(elem, $(this)).offset().top);- cumprimentos
Luke Snowden
1
Se você quiser deixar alguma margem superior entre a página eo elemento (que pode parecer melhor em alguns casos), subtrair, por exemplo, 10px, como: $(this).scrollTop() - $(this).offset().top + $(elem).offset().top - 10.
Lepe
Se você está tendo diferenças de posição entre Firefox e Chrome (ou outros navegadores), verificar o seu código html (ver este relatório de bug )
Lepe
34

Ajustei a resposta de Glenn Moss para levar em consideração o fato de que a div de estouro pode não estar no topo da página.

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

Eu estava usando isso em um aplicativo do Google Maps com um modelo responsivo. Na resolução> 800px, a lista estava no lado esquerdo do mapa. Na resolução <800, a lista estava abaixo do mapa.

KPheasey
fonte
6

As respostas acima posicionarão o elemento interno na parte superior do elemento de transbordamento, mesmo que esteja dentro do elemento de transbordamento. Eu não queria isso, então o modifiquei para não alterar a posição de rolagem se o elemento estiver em exibição.

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
fonte
Isso me ajudou !! Consulte stackoverflow.com/questions/48283932/…
gene b.
1

Escrevo estas 2 funções para facilitar minha vida:

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

E use-os:

scrollToTop($innerListItem, $parentDiv, 200);
// or
scrollToCenter($innerListItem, $parentDiv, 200);
trank
fonte
elem.offset().topestá calculando a partir do topo da tela. Eu quero que calcular a partir do elemento pai. Como faço isso ?
Santosh
0

Depois de jogar por um longo tempo, é isso que eu criei:

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

e eu chamo assim

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