Comment faire défiler jusqu'à un élément à l'intérieur d'un div?

171

J'ai un défilement divet je veux avoir un lien lorsque je clique dessus, cela le forcera divà faire défiler pour voir un élément à l'intérieur. J'ai écrit son JavasSript comme ceci:

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

mais cela fait défiler toute la page tout en faisant défiler divelle - même. Comment y remédier?

Je veux le dire comme ça

MyContainerDiv.getElementById(chr).scrollIntoView(true);
Amr Elgarhy
la source
Scrolltop ne renvoie pas toujours une valeur utilisable. J'ai tendance à utiliser un SetTimeoutdans la $(document).ready({})fonction et à définir focus()l'élément vers lequel vous souhaitez faire défiler. Works for me
DerpyNerd

Réponses:

343

Vous devez obtenir le décalage supérieur de l'élément dans lequel vous souhaitez faire défiler la vue, par rapport à son parent (le conteneur div défilant):

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

La variable topPos est maintenant définie sur la distance entre le haut du div défilant et l'élément que vous souhaitez voir visible (en pixels).

Maintenant, nous disons au div de faire défiler jusqu'à cette position en utilisant scrollTop:

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

Si vous utilisez le prototype de framework JS, vous feriez la même chose comme ceci:

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

Encore une fois, cela fera défiler le div afin que l'élément que vous souhaitez voir soit exactement en haut (ou si ce n'est pas possible, défilez aussi loin que possible pour qu'il soit visible).

Brian Barrett
la source
9
C'était vraiment utile! Si vous souhaitez régler le défilement plusieurs fois, vous devez compenser par votre emplacement de défilement actuel. Voici comment je l'ai fait dans jQuery: $ ('# scrolling_div'). ScrollTop ($ ('# scrolling_div'). ScrollTop () + $ ('# element_within_div'). Position (). Top);
Sera
3
Méfiez-vous que la demande myElement.offsetTopdéclenchera une redistribution (mise en page thrashing) qui pourrait être un goulot d'étranglement des performances
Kestutis
27
N'oubliez pas de définir le parent de défilement avec css: position: relativesinon vous passerez beaucoup de temps à déboguer comme je viens de le faire.
saveario
2
J'ai dû définir la overflow-ypropriété sur scrollpour l'élément parent ( scrolling_div), sinon cela ne fonctionnait pas. La valeur css par défaut de la overflowpropriété est autoet même si elle rend également possible le défilement manuel, le code js ne fonctionnerait pas (même pas avec {psition: relative}..)
Evgenia Manolova
2
Fonctionne toujours en 2017. Informations supplémentaires: .offsetTop peut renvoyer 0. Ensuite, vous devez vous référer à un élément parent et réessayer. Je l' ai fait pour les balises h4alors divalors articletag et seulement articletravaillé pour moi.
Fenio
66

Vous devrez trouver la position de l'élément dans le DIV vers lequel vous voulez faire défiler et définir la propriété scrollTop.

divElem.scrollTop = 0;

Mise à jour :

Exemple de code pour monter ou descendre

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

  function move_down() {
    document.getElementById('divElem').scrollTop -= 10;
  }
Glennular
la source
3
Je veux le faire défiler, pour le voir ne pas défiler avec une certaine valeur
Amr Elgarhy
39

Méthode 1 - Défilement fluide vers un élément à l'intérieur d'un élément

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>

Méthode 2 - Utilisation de Element.scrollIntoView :

Notez que la prise en charge du navigateur n'est pas géniale pour celui-ci

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>

Méthode 3 - Utilisation du comportement de défilement 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
la source
1
Notez que le comportement de défilement n'est pas pris en charge dans IE / Edge / Safari
sheats
1
@sheats - cela dit exactement cela dans le lien de documentation MDN que j'avais placé en grande taille de police. Même si cela ne fonctionne pas pour ces navigateurs, cela ne signifie pas que vous ne devriez pas l'utiliser. Il n'y a pas de "règle" tout doit se comporter de la même manière sur tous les navigateurs. Si les navigateurs modernes peuvent faire de la magie, laissez-les faire de la magie.
vsync
Cet article concerne le défilement du document entier plutôt qu'un élément déroulant.
@ DoMiNeLa10 - C'est une hypothèse. OP aurait pu fournir un exemple arbitraire destiné à illustrer son problème. De plus, OP n'est pas la principale préoccupation, mais plutôt les personnes issues des moteurs de recherche à la recherche d'une solution saine, et répondre très probablement spécifiquement aux besoins des OP ne les aidera pas, et le but de ce site Web est de créer des réponses solides qui peuvent aider le plus grand nombre. que possible. Ma réponse fournit les deux .
vsync
12

Pour faire défiler un élément dans la vue d'un div, uniquement si nécessaire, vous pouvez utiliser cette scrollIfNeededfonction:

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
la source
1
C'était vraiment utile. J'ai eu un peu de mal car j'avais raté la position: relative sur le conteneur. C'était crucial!
brohr
11

Le code doit être:

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

Vous voulez faire défiler la différence entre la position supérieure de l'enfant et la position supérieure de div.

Accédez aux éléments enfants en utilisant:

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

etc....

pgp
la source
1
La deuxième ligne ne devrait-elle pas être lue var chElem = document.getElementById('element_within_div');et la troisième ligne ne devrait-elle pas être lue var topPos = divElem.offsetTop;?
jayp
7

Si vous utilisez jQuery, vous pouvez faire défiler avec une animation en utilisant ce qui suit:

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

L'animation est facultative: vous pouvez également prendre la valeur scrollTop calculée ci-dessus et la placer directement dans la propriété scrollTop du conteneur .

dlauzon
la source
5

JS natif, navigateur croisé, défilement fluide (mise à jour 2020)

Le réglage ScrollTopdonne le résultat souhaité mais le défilement est très brusque. Utiliser jquerypour avoir un défilement fluide n'était pas une option. Voici donc un moyen natif de faire le travail qui prend en charge tous les principaux navigateurs. Référence - 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'});

C'est tout!


Et voici un extrait de travail pour les douteux -

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>

Mise à jour: comme vous pouvez le constater dans les commentaires, il semble que ce Element.scrollTo()ne soit pas pris en charge dans IE11. Donc, si vous ne vous souciez pas d'IE11 (vous ne devriez vraiment pas), n'hésitez pas à l'utiliser dans tous vos projets. Notez que le support existe pour Edge! Vous ne laissez donc pas vraiment vos utilisateurs Edge / Windows derrière;)

Référence

Niket Pathak
la source
1
scrollTo()peut être pris en charge dans tous les principaux navigateurs pour les Windowobjets mais n'est pas pris en charge dans IE ou Edge pour les éléments.
Tim Down le
Selon caniuse , il est pris en charge dans IE11 et Edge. Je n'ai pas testé personnellement sur ces navigateurs, mais il semble être pris en charge.
Niket Pathak le
1
Ce n'est window.scrollTopas ça Element.scrollTo. Essayez ceci dans Edge, par exemple, et vérifiez la console: codepen.io/timdown/pen/abzVEMB
Tim Down
vous avez raison. IE11 n'est pas pris en charge. Cependant Edge v76 (ref) et supérieur semble être pris en charge
Niket Pathak
2

Il y a deux faits:

1) Le composant scrollIntoView n'est pas pris en charge par safari.

2) JS framework jQuery peut faire le travail comme ceci:

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

$(parent).animate({scrollTop: $(child).offset().top}, duration)
nickmit
la source
1

Voici une solution JavaScript pure et simple qui fonctionne pour un nombre cible (valeur pour scrollTop), un élément DOM cible ou certains cas de chaîne spéciaux:

/**
 * 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;
    }
};

Et voici quelques exemples d'utilisation:

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

ou

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

ou

// Scroll to targetElement
var scrollableDiv = document.getElementById('scrollable_div');
var targetElement= document.getElementById('target_element');
scrollToTarget(targetElement, scrollableDiv);
NoBrainer
la source
1

Un autre exemple d'utilisation de jQuery et 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");
    }
});
Espoir
la source
0

Défilement animé par l'utilisateur

Voici un exemple de comment faire défiler un <div>horizontalement par programme, sans JQuery . Pour faire défiler verticalement, vous remplaceriez les écritures de JavaScript scrollLeftpar scrollTop, à la place.

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

Crédit à Dux .


Défilement animé automatique

De plus, voici des fonctions permettant de faire défiler <div>complètement vers la gauche et la droite. La seule chose que nous changeons ici, c'est que nous vérifions si l'extension complète du scroll a été utilisée avant de faire un appel récursif pour faire défiler à nouveau.

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
la source
0

C'est ce qui m'a finalement servi

/** 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
la source
0

le navigateur fait défiler automatiquement jusqu'à un élément qui obtient le focus, donc ce que vous pouvez également faire pour envelopper l'élément dans lequel vous devez faire défiler <a>...</a>, puis lorsque vous avez besoin de faire défiler, définissez simplement le focus sur celaa

Trident D'Gao
la source
0

Après avoir sélectionné l'élément, utilisez simplement la scrollIntoViewfonction avec l'option ci-dessous:

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

La réponse à cet article est donc:

const el = document.getElementById('id-name');
el.scrollIntoView({
  top: 0,
  left: 0,
  behavior: 'auto',
});
AmerllicA
la source
0

étant donné que vous avez un élément div dont vous avez besoin pour faire défiler l'intérieur, essayez ce morceau de code

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

cela fonctionne avec moi à l'intérieur d'un div avec un parchemin, cela devrait fonctionner avec vous au cas où vous auriez pointé la souris sur cet élément et ensuite essayé de faire défiler vers le bas ou vers le haut. Si cela fonctionne manuellement, cela devrait fonctionner aussi

Amado Saladino
la source