Jak przewinąć do elementu wewnątrz div?

170

Mam przewijany divi chcę mieć link, kiedy go kliknę, wymusi to divprzewinięcie w celu wyświetlenia elementu wewnątrz. Napisałem JavasSript w ten sposób:

document.getElementById(chr).scrollIntoView(true);

ale to przewija całą stronę podczas przewijania divsamej strony. Jak to naprawić?

Chcę to tak powiedzieć

MyContainerDiv.getElementById(chr).scrollIntoView(true);
Amr Elgarhy
źródło
Scrolltop nie zawsze zwraca użyteczną wartość. Zwykle używam a SetTimeoutw $(document).ready({})funkcji i ustawiam focus()element, do którego chcesz przewinąć. Działa dla mnie
DerpyNerd

Odpowiedzi:

341

Musisz uzyskać górne przesunięcie elementu, który chcesz przewinąć, względem jego elementu nadrzędnego (przewijanego kontenera div):

var myElement = document.getElementById('element_within_div');
var topPos = myElement.offsetTop;

Zmienna topPos jest teraz ustawiona na odległość między górną częścią przewijanego elementu div a elementem, który ma być widoczny (w pikselach).

Teraz mówimy elementowi DIV, aby przewinął do tej pozycji za pomocą scrollTop:

document.getElementById('scrolling_div').scrollTop = topPos;

Jeśli używasz prototypowego frameworka JS, zrobiłbyś to samo:

var posArray = $('element_within_div').positionedOffset();
$('scrolling_div').scrollTop = posArray[1];

Ponownie, spowoduje to przewinięcie elementu div, tak aby element, który chcesz zobaczyć, znajdował się dokładnie na górze (lub jeśli nie jest to możliwe, przewinięty tak głęboko, jak to możliwe, aby był widoczny).

Brian Barrett
źródło
9
To było naprawdę pomocne! Jeśli chcesz ustawić przewijanie wiele razy, musisz przesunąć w zależności od bieżącej lokalizacji przewijania. Oto jak zrobiłem to w jQuery: $ ('# scrolling_div'). ScrollTop ($ ('# scrolling_div'). ScrollTop () + $ ('# element_within_div'). Position (). Top);
Będzie
3
Pamiętaj, że zainteresowanie myElement.offsetTopreflow wyzwoli (cięgi układ), która mogłaby być wąskim gardłem wydajności
Kestutis
27
Pamiętaj, aby ustawić przewijanego rodzica na css: w position: relativeprzeciwnym razie spędzisz dużo czasu na debugowaniu, tak jak właśnie to zrobiłem.
saveario
2
Musiałem ustawić overflow-ywłaściwość scrolldla elementu nadrzędnego ( scrolling_div), w przeciwnym razie nie działała. Domyślna wartość css dla overflowwłaściwości to autoi chociaż umożliwia również ręczne przewijanie, kod js nie działałby (nawet z {psition: relative}…)
Evgenia Manolova
2
Nadal działa w 2017. Dodatkowe informacje: .offsetTop może zwrócić 0. Następnie powinieneś odwołać się do elementu nadrzędnego i spróbować ponownie. Zrobiłem to dla tagów, h4a divpotem articletag i articledziałało tylko dla mnie.
Fenio,
66

Musiałbyś znaleźć pozycję elementu w DIV, do którego chcesz przewinąć, i ustawić właściwość scrollTop.

divElem.scrollTop = 0;

Aktualizacja :

Przykładowy kod do poruszania się w górę lub w dół

  function move_up() {
    document.getElementById('divElem').scrollTop += 10;
  }

  function move_down() {
    document.getElementById('divElem').scrollTop -= 10;
  }
Glennular
źródło
3
chcę go przewinąć, aby zobaczyć, że nie przewija się z określoną wartością
Amr Elgarhy
39

Metoda 1 - Płynne przewijanie do elementu wewnątrz elementu

var box = document.querySelector('.box'),
    targetElm = document.querySelector('.boxChild'); // <-- Scroll to here within ".box"

document.querySelector('button').addEventListener('click', function(){
   scrollToElm( box, targetElm , 600 );   
});


/////////////

function scrollToElm(container, elm, duration){
  var pos = getRelativePos(elm);
  scrollTo( container, pos.top , 2);  // duration in seconds
}

function getRelativePos(elm){
  var pPos = elm.parentNode.getBoundingClientRect(), // parent pos
      cPos = elm.getBoundingClientRect(), // target pos
      pos = {};

  pos.top    = cPos.top    - pPos.top + elm.parentNode.scrollTop,
  pos.right  = cPos.right  - pPos.right,
  pos.bottom = cPos.bottom - pPos.bottom,
  pos.left   = cPos.left   - pPos.left;

  return pos;
}
    
function scrollTo(element, to, duration, onDone) {
    var start = element.scrollTop,
        change = to - start,
        startTime = performance.now(),
        val, now, elapsed, t;

    function animateScroll(){
        now = performance.now();
        elapsed = (now - startTime)/1000;
        t = (elapsed/duration);

        element.scrollTop = start + change * easeInOutQuad(t);

        if( t < 1 )
            window.requestAnimationFrame(animateScroll);
        else
            onDone && onDone();
    };

    animateScroll();
}

function easeInOutQuad(t){ return t<.5 ? 2*t*t : -1+(4-2*t)*t };
.box{ width:80%; border:2px dashed; height:180px; overflow:auto; }
.boxChild{ 
  margin:600px 0 300px; 
  width: 40px;
  height:40px;
  background:green;
}
<button>Scroll to element</button>
<div class='box'>
  <div class='boxChild'></div>
</div>

Metoda 2 - Korzystanie z Element.scrollIntoView :

Pamiętaj, że obsługa przeglądarki nie jest świetna w tym przypadku

var targetElm = document.querySelector('.boxChild'),  // reference to scroll target
    button = document.querySelector('button');        // button that triggers the scroll
  
// bind "click" event to a button 
button.addEventListener('click', function(){
   targetElm.scrollIntoView()
})
.box {
  width: 80%;
  border: 2px dashed;
  height: 180px;
  overflow: auto;
  scroll-behavior: smooth; /* <-- for smooth scroll */
}

.boxChild {
  margin: 600px 0 300px;
  width: 40px;
  height: 40px;
  background: green;
}
<button>Scroll to element</button>
<div class='box'>
  <div class='boxChild'></div>
</div>

Metoda 3 - Używanie zachowania przewijania CSS :

.box {
  width: 80%;
  border: 2px dashed;
  height: 180px;
  overflow-y: scroll;
  scroll-behavior: smooth; /* <--- */
}

#boxChild {
  margin: 600px 0 300px;
  width: 40px;
  height: 40px;
  background: green;
}
<a href='#boxChild'>Scroll to element</a>
<div class='box'>
  <div id='boxChild'></div>
</div>

vsync
źródło
1
Zwróć uwagę, że zachowanie przewijania nie jest obsługiwane w przeglądarce IE / Edge / Safari
obejmuje
1
@sheats - mówi dokładnie to w linku do dokumentacji MDN, który umieściłem dużą czcionką. Nawet jeśli nie działa w tych przeglądarkach, nie oznacza to, że nie powinieneś go używać. Nie ma „reguły”, wszystko musi zachowywać się tak samo we wszystkich przeglądarkach. Jeśli współczesne przeglądarki potrafią robić magię, pozwól im robić magię.
vsync
Ten post dotyczy przewijania całego dokumentu, a nie elementu przewijalnego.
@ DoMiNeLa10 - To założenie. OP mógł podać dowolny przykład, który miał zilustrować jego / jej problem. Poza tym OP nie jest głównym problemem, ale raczej ludzie pochodzący z wyszukiwarek szukający zdrowego rozwiązania i najprawdopodobniej odpowiadanie konkretnie na potrzeby OP nie pomoże im, a celem tej witryny jest stworzenie solidnych odpowiedzi, które mogą pomóc tak wielu jak to możliwe. Moja odpowiedź dostarcza obu .
vsync
12

Aby przewinąć element do widoku div, tylko w razie potrzeby, możesz użyć tej scrollIfNeededfunkcji:

function scrollIfNeeded(element, container) {
  if (element.offsetTop < container.scrollTop) {
    container.scrollTop = element.offsetTop;
  } else {
    const offsetBottom = element.offsetTop + element.offsetHeight;
    const scrollBottom = container.scrollTop + container.offsetHeight;
    if (offsetBottom > scrollBottom) {
      container.scrollTop = offsetBottom - container.offsetHeight;
    }
  }
}

document.getElementById('btn').addEventListener('click', ev => {
  ev.preventDefault();
  scrollIfNeeded(document.getElementById('goose'), document.getElementById('container'));
});
.scrollContainer {
  overflow-y: auto;
  max-height: 100px;
  position: relative;
  border: 1px solid red;
  width: 120px;
}

body {
  padding: 10px;
}

.box {
  margin: 5px;
  background-color: yellow;
  height: 25px;
  display: flex;
  align-items: center;
  justify-content: center;
}

#goose {
  background-color: lime;
}
<div id="container" class="scrollContainer">
  <div class="box">duck</div>
  <div class="box">duck</div>
  <div class="box">duck</div>
  <div class="box">duck</div>
  <div class="box">duck</div>
  <div class="box">duck</div>
  <div class="box">duck</div>
  <div class="box">duck</div>
  <div id="goose" class="box">goose</div>
  <div class="box">duck</div>
  <div class="box">duck</div>
  <div class="box">duck</div>
  <div class="box">duck</div>
</div>

<button id="btn">scroll to goose</button>

mpen
źródło
1
To było naprawdę pomocne. Trochę się zmagałem, ponieważ przegapiłem pozycję: względną na pojemniku. To było kluczowe!
brohr
11

Kod powinien być:

var divElem = document.getElementById('scrolling_div');
var chElem = document.getElementById('element_within_div');
var topPos = divElem.offsetTop;
divElem.scrollTop = topPos - chElem.offsetTop;

Chcesz przewinąć różnicę między górną pozycją podrzędną a najwyższą pozycją div.

Uzyskaj dostęp do elementów potomnych za pomocą:

var divElem = document.getElementById('scrolling_div'); 
var numChildren = divElem.childNodes.length;

i tak dalej....

pgp
źródło
1
Czy druga linia nie powinna faktycznie czytać, var chElem = document.getElementById('element_within_div');a trzecia linia czytać var topPos = divElem.offsetTop;?
jayp
7

Jeśli korzystasz z jQuery, możesz przewijać animację za pomocą:

$(MyContainerDiv).animate({scrollTop: $(MyContainerDiv).scrollTop() + ($('element_within_div').offset().top - $(MyContainerDiv).offset().top)});

Animacja jest opcjonalna: możesz również wziąć obliczoną powyżej wartość scrollTop i umieścić ją bezpośrednio we właściwości scrollTop kontenera .

dlauzon
źródło
5

Natywny JS, Cross Browser, Smooth Scroll (aktualizacja 2020)

Ustawienie ScrollTopdaje pożądany rezultat, ale przewijanie jest bardzo gwałtowne. Używanie jquerypłynnego przewijania nie wchodziło w grę. Oto natywny sposób wykonania zadania, który obsługuje wszystkie główne przeglądarki. Odniesienie - caniuse

// get the "Div" inside which you wish to scroll (i.e. the container element)
const El = document.getElementById('xyz');

// Lets say you wish to scroll by 100px, 
El.scrollTo({top: 100, behavior: 'smooth'});

// If you wish to scroll until the end of the container
El.scrollTo({top: El.scrollHeight, behavior: 'smooth'});

Otóż ​​to!


A oto działający fragment dla wątpliwych -

document.getElementById('btn').addEventListener('click', e => {
  e.preventDefault();
  // smooth scroll
  document.getElementById('container').scrollTo({top: 175, behavior: 'smooth'});
});
/* just some styling for you to ignore */
.scrollContainer {
  overflow-y: auto;
  max-height: 100px;
  position: relative;
  border: 1px solid red;
  width: 120px;
}

body {
  padding: 10px;
}

.box {
  margin: 5px;
  background-color: yellow;
  height: 25px;
  display: flex;
  align-items: center;
  justify-content: center;
}

#goose {
  background-color: lime;
}
<!-- Dummy html to be ignored -->
<div id="container" class="scrollContainer">
  <div class="box">duck</div>
  <div class="box">duck</div>
  <div class="box">duck</div>
  <div class="box">duck</div>
  <div class="box">duck</div>
  <div class="box">duck</div>
  <div class="box">duck</div>
  <div class="box">duck</div>
  <div id="goose" class="box">goose</div>
  <div class="box">duck</div>
  <div class="box">duck</div>
  <div class="box">duck</div>
  <div class="box">duck</div>
</div>

<button id="btn">goose</button>

Aktualizacja: Jak widać w komentarzach, wygląda na to, że Element.scrollTo()IE11 nie jest obsługiwany. Więc jeśli nie obchodzi cię IE11 (naprawdę nie powinieneś), możesz go używać we wszystkich swoich projektach. Zauważ, że istnieje wsparcie dla Edge! Więc tak naprawdę nie pozostawiasz użytkowników Edge / Windows w tyle;)

Odniesienie

Niket Pathak
źródło
1
scrollTo()może być obsługiwany we wszystkich głównych przeglądarkach dla Windowobiektów, ale nie jest obsługiwany w IE lub Edge dla elementów.
Tim Down
Według caniuse jest obsługiwany w IE11 i Edge. Nie testowałem osobiście w tych przeglądarkach, ale wydaje się, że jest obsługiwane.
Niket Pathak
1
To window.scrollTonie Element.scrollTo. Wypróbuj na przykład w Edge i sprawdź konsolę: codepen.io/timdown/pen/abzVEMB
Tim Down
masz rację. IE11 nie jest obsługiwany. Jednak Edge v76 (ref) i nowsze wydają się być obsługiwane
Niket Pathak
2

Istnieją dwa fakty:

1) Component scrollIntoView nie jest obsługiwany przez safari.

2) JS framework jQuery może wykonać to zadanie:

parent = 'some parent div has css position==="fixed"' || 'html, body';

$(parent).animate({scrollTop: $(child).offset().top}, duration)
nickmit
źródło
1

Oto proste rozwiązanie w czystym języku JavaScript, które działa w przypadku docelowej liczby (wartości scrollTop), docelowego elementu DOM lub niektórych specjalnych przypadków typu String:

/**
 * target - target to scroll to (DOM element, scrollTop Number, 'top', or 'bottom'
 * containerEl - DOM element for the container with scrollbars
 */
var scrollToTarget = function(target, containerEl) {
    // Moved up here for readability:
    var isElement = target && target.nodeType === 1,
        isNumber = Object.prototype.toString.call(target) === '[object Number]';

    if (isElement) {
        containerEl.scrollTop = target.offsetTop;
    } else if (isNumber) {
        containerEl.scrollTop = target;
    } else if (target === 'bottom') {
        containerEl.scrollTop = containerEl.scrollHeight - containerEl.offsetHeight;
    } else if (target === 'top') {
        containerEl.scrollTop = 0;
    }
};

A oto kilka przykładów użycia:

// Scroll to the top
var scrollableDiv = document.getElementById('scrollable_div');
scrollToTarget('top', scrollableDiv);

lub

// Scroll to 200px from the top
var scrollableDiv = document.getElementById('scrollable_div');
scrollToTarget(200, scrollableDiv);

lub

// Scroll to targetElement
var scrollableDiv = document.getElementById('scrollable_div');
var targetElement= document.getElementById('target_element');
scrollToTarget(targetElement, scrollableDiv);
NoBrainer
źródło
1

Kolejny przykład wykorzystania jQuery i animate.

var container = $('#container');
var element = $('#element');

container.animate({
    scrollTop: container.scrollTop = container.scrollTop() + element.offset().top - container.offset().top
}, {
    duration: 1000,
    specialEasing: {
        width: 'linear',
        height: 'easeOutBounce'
    },
    complete: function (e) {
        console.log("animation completed");
    }
});
Mam nadzieję
źródło
0

Przewijanie animowane przez użytkownika

Oto przykład, jak programowo przewijać w <div>poziomie, bez JQuery . Aby przewijać w pionie, należy wymienić pisze JavaScript w przeglądarce, by scrollLeftze scrollTopzamiast.

JSFiddle

https://jsfiddle.net/fNPvf/38536/

HTML

<!-- Left Button. -->
<div style="float:left;">
    <!-- (1) Whilst it's pressed, increment the scroll. When we release, clear the timer to stop recursive scroll calls. -->
    <input type="button" value="«" style="height: 100px;" onmousedown="scroll('scroller',3, 10);" onmouseup="clearTimeout(TIMER_SCROLL);"/>
</div>
<!-- Contents to scroll. -->
<div id="scroller" style="float: left; width: 100px; height: 100px; overflow: hidden;">
    <!-- <3 -->
    <img src="https://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/so-logo.png?v=9c558ec15d8a" alt="image large" style="height: 100px" />
</div>
<!-- Right Button. -->
<div style="float:left;">
    <!-- As (1). (Use a negative value of 'd' to decrease the scroll.) -->
    <input type="button" value="»" style="height: 100px;" onmousedown="scroll('scroller',-3, 10);" onmouseup="clearTimeout(TIMER_SCROLL);"/>
</div>

JavaScript

// Declare the Shared Timer.
var TIMER_SCROLL;
/** 
Scroll function. 
@param id  Unique id of element to scroll.
@param d   Amount of pixels to scroll per sleep.
@param del Size of the sleep (ms).*/
function scroll(id, d, del){
    // Scroll the element.
    document.getElementById(id).scrollLeft += d;
    // Perform a delay before recursing this function again.
    TIMER_SCROLL = setTimeout("scroll('"+id+"',"+d+", "+del+");", del);
 }

Kredyt dla Dux .


Automatyczne przewijanie animowane

Ponadto są tutaj funkcje umożliwiające przewijanie w <div>pełni w lewo iw prawo. Jedyną rzeczą, którą tutaj zmieniamy, jest sprawdzenie, czy pełne rozszerzenie zwoju zostało wykorzystane przed ponownym wywołaniem rekurencyjnego przewijania.

JSFiddle

https://jsfiddle.net/0nLc2fhh/1/

HTML

<!-- Left Button. -->
<div style="float:left;">
    <!-- (1) Whilst it's pressed, increment the scroll. When we release, clear the timer to stop recursive scroll calls. -->
    <input type="button" value="«" style="height: 100px;" onclick="scrollFullyLeft('scroller',3, 10);"/>
</div>
<!-- Contents to scroll. -->
<div id="scroller" style="float: left; width: 100px; height: 100px; overflow: hidden;">
  <!-- <3 -->
  <img src="https://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/so-logo.png?v=9c558ec15d8a" alt="image large" style="height: 100px" />
</div>
<!-- Right Button. -->
<div style="float:left;">
    <!-- As (1). (Use a negative value of 'd' to decrease the scroll.) -->
    <input type="button" value="»" style="height: 100px;" onclick="scrollFullyRight('scroller',3, 10);"/>
</div>

JavaScript

// Declare the Shared Timer.
var TIMER_SCROLL;
/** 
Scroll fully left function; completely scrolls  a <div> to the left, as far as it will go.
@param id  Unique id of element to scroll.
@param d   Amount of pixels to scroll per sleep.
@param del Size of the sleep (ms).*/
function scrollFullyLeft(id, d, del){
    // Fetch the element.
    var el = document.getElementById(id);
    // Scroll the element.
    el.scrollLeft += d;
    // Have we not finished scrolling yet?
    if(el.scrollLeft < (el.scrollWidth - el.clientWidth)) {
        TIMER_SCROLL = setTimeout("scrollFullyLeft('"+id+"',"+d+", "+del+");", del);
    }
}

/** 
Scroll fully right function; completely scrolls  a <div> to the right, as far as it will go.
@param id  Unique id of element to scroll.
@param d   Amount of pixels to scroll per sleep.
@param del Size of the sleep (ms).*/
function scrollFullyRight(id, d, del){
    // Fetch the element.
    var el = document.getElementById(id);
    // Scroll the element.
    el.scrollLeft -= d;
    // Have we not finished scrolling yet?
    if(el.scrollLeft > 0) {
        TIMER_SCROLL = setTimeout("scrollFullyRight('"+id+"',"+d+", "+del+");", del);
    }
}
Mapsy
źródło
0

To właśnie mi w końcu służyło

/** Set parent scroll to show element
 * @param element {object} The HTML object to show
 * @param parent {object} The HTML object where the element is shown  */
var scrollToView = function(element, parent) {
    //Algorithm: Accumulate the height of the previous elements and add half the height of the parent
    var offsetAccumulator = 0;
    parent = $(parent);
    parent.children().each(function() {
        if(this == element) {
            return false; //brake each loop
        }
        offsetAccumulator += $(this).innerHeight();
    });
    parent.scrollTop(offsetAccumulator - parent.innerHeight()/2);
}
Iñigo Panera
źródło
0

przeglądarka przewija automatycznie do elementu, który ma fokus, więc co możesz zrobić, aby zawinąć element, do którego <a>...</a>chcesz przewinąć, a kiedy potrzebujesz przewijania, po prostu ustaw na tym fokusa

Trident D'Gao
źródło
0

Po wybraniu elementu wystarczy skorzystać scrollIntoViewz funkcji z poniższą opcją:

const option = {
  top: 0, // number,
  left: 0, // number,
  behavior: 'auto', // auto or smooth 
    // - auto for one jump motion and smooth for animated motion -
};

Tak więc odpowiedź na ten post brzmi:

const el = document.getElementById('id-name');
el.scrollIntoView({
  top: 0,
  left: 0,
  behavior: 'auto',
});
AmerllicA
źródło
0

biorąc pod uwagę, że masz element div, który musisz przewijać w środku, wypróbuj ten fragment kodu

document.querySelector('div').scroll(x,y)

to działa ze mną wewnątrz elementu div ze zwojem, powinno to działać w przypadku, gdy wskazałeś kursorem myszy na ten element, a następnie próbowałeś przewijać w dół lub w górę. Jeśli działa ręcznie, powinno też działać

Amado Saladino
źródło