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


181

J'ai un widget de chat qui affiche un tableau de messages à chaque fois que je fais défiler vers le haut. Le problème auquel je suis confronté maintenant est que le curseur reste fixe en haut lorsque les messages se chargent. Je veux qu'il se concentre sur le dernier élément d'index du tableau précédent. J'ai compris que je pouvais créer des références dynamiques en passant un index, mais j'aurais également besoin de savoir quel type de fonction de défilement utiliser pour y parvenir

 handleScrollToElement(event) {
    const tesNode = ReactDOM.findDOMNode(this.refs.test)
    if (some_logic){
      //scroll to testNode      
    }
  }

  render() {

    return (
      <div>
        <div ref="test"></div>
      </div>)
  }

1
Pour une solution groupée: npmjs.com/package/react-scroll-to-component
tokland

Réponses:


303

React 16.8 +, composant fonctionnel

import React, { useRef } from 'react'

const scrollToRef = (ref) => window.scrollTo(0, ref.current.offsetTop)   
// General scroll to element function

const ScrollDemo = () => {

   const myRef = useRef(null)
   const executeScroll = () => scrollToRef(myRef)

   return (
      <> 
         <div ref={myRef}>I wanna be seen</div> 
         <button onClick={executeScroll}> Click to scroll </button> 
      </>
   )
}

Cliquez ici pour une démo complète sur StackBlits

React 16.3 +, composant de classe

class ReadyToScroll extends Component {

    constructor(props) {
        super(props)
        this.myRef = React.createRef()  
    }

    render() {
        return <div ref={this.myRef}></div> 
    }  

    scrollToMyRef = () => window.scrollTo(0, this.myRef.current.offsetTop)   
    // run this method to execute scrolling. 

}

Composant de classe - Rappel de référence

class ReadyToScroll extends Component {
    myRef=null
    // Optional

    render() {
        return <div ref={ (ref) => this.myRef=ref }></div>
    } 

    scrollToMyRef = () => window.scrollTo(0, this.myRef.offsetTop)
    // run this method to execute scrolling. 
}

N'utilisez pas de références String.

Les références de chaîne nuisent aux performances, ne sont pas composables et sont en voie de disparition (août 2018).

Les références de chaîne ont quelques problèmes, sont considérées comme héritées et sont susceptibles d'être supprimées dans l'une des futures versions. [Documentation officielle React]

ressource1 ressource2

Facultatif: animation de défilement lisse

/* css */
html {
    scroll-behavior: smooth;
}

Passer ref à un enfant

Nous voulons que la référence soit attachée à un élément dom, pas à un composant de réaction. Ainsi, lorsque nous le passons à un composant enfant, nous ne pouvons pas nommer la référence prop.

const MyComponent = () => {
    const myRef = useRef(null)
    return <ChildComp refProp={myRef}></ChildComp>
} 

Ensuite, attachez le prop de référence à un élément dom.

const ChildComp = (props) => {
    return <div ref={props.refProp} />
}

5
window.scrollTo(0, offsetTop)est une meilleure option avec un meilleur support parmi les navigateurs actuels
MoMo

1
Pourrait vous assurer que vous êtes cohérent dans votre exemple. Nous partons de myRef, allons avec domRef et finissons par tesNode?. C'est assez déroutant
Louis Lecocq

4
Évident après coup, mais il est important de mentionner que cela ne fonctionne que pour les éléments DOM natifs et pas n'importe quel composant React.
jpunk11

1
@ jpunk11 Je viens de mettre à jour ma réponse. La réponse mise à jour explique comment faire défiler jusqu'à un élément dom qui se trouve dans un composant de classe enfant.
Ben Carp

2
@SimonFranzen Jetez un œil à ma réponse mise à jour - TLDR - cas de composant de classe. Lorsque scrollToMyRef est appelé, il défilera jusqu'à l'enfant auquel vous avez attaché la référence. Vous pouvez transmettre la méthode à un autre composant enfant et le déclencher à partir de là.
Ben Carp

55

cela a fonctionné pour moi

this.anyRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' })

EDIT: Je voulais développer cela en fonction des commentaires.

const scrollTo = (ref) => {
  if (ref /* + other conditions */) {
    ref.scrollIntoView({ behavior: 'smooth', block: 'start' })
  }
}

<div ref={scrollTo}>Item</div>

1
où mettre ça
su_sundariya

dans la méthode du cycle de vie ou le constructeur
su_sundariya

1
Fonctionne comme un charme. Rien de ce qui précède ne fonctionne pour moi, cela devrait être une réponse acceptée!
Shin

1
Cela a fonctionné pour moi, notez simplement que «start» est la valeur par défaut du paramètre «block».
Liron Lavi

Cela a fonctionné pour moi quand la réponse de @Ben Carp n'a pas fonctionné.
Jason Masters

37

Trouvez simplement la position supérieure de l'élément que vous avez déjà déterminé https://www.w3schools.com/Jsref/prop_element_offsettop.asp puis faites défiler jusqu'à cette position via la scrollTométhode https://www.w3schools.com/Jsref/met_win_scrollto.asp

Quelque chose comme ça devrait fonctionner:

handleScrollToElement(event) {
  const tesNode = ReactDOM.findDOMNode(this.refs.test)
  if (some_logic){
    window.scrollTo(0, tesNode.offsetTop);
  }
}

render() {

  return (
    <div>
      <div ref="test"></div>
    </div>)
}

METTRE À JOUR:

depuis React v16.3 le React.createRef()est préféré

constructor(props) {
  super(props);
  this.myRef = React.createRef();
}

handleScrollToElement(event) {
  if (<some_logic>){
    window.scrollTo(0, this.myRef.current.offsetTop);
  }
}

render() {

  return (
    <div>
      <div ref={this.myRef}></div>
    </div>)
}

2
C'est la meilleure réponse. L'utilisation ReactDOM.findDomNode()est une meilleure pratique - puisque React restitue les composants, un div que vous obtenez simplement par son ID peut ne pas exister au moment où vous appelez la fonction
Good Idea

4
Selon la documentation officielle, vous devriez essayer d'éviter d'utiliser findDOMNode. Dans la plupart des cas, vous pouvez attacher une référence au nœud DOM et éviter d'utiliser findDOMNodedu tout.
Facyo Kouch

1
Notez que l'utilisation de this.refs par mappage de chaînes est obsolète, voir: stackoverflow.com/questions/43873511/…
Himmet Avsar

1
Remarque: j'ai dû utiliser à la this.myRef.current.scrollIntoView()place de window.scrollTo(0, this.myRef).
Babbz77

14

L'utilisation de findDOMNode sera finalement obsolète.

La méthode préférée consiste à utiliser des références de rappel.

github eslint


3
Veuillez inclure la partie pertinente du matériel lié afin que votre réponse ne devienne pas inutile au cas où elle serait supprimée.
totymedli

12

Vous pouvez maintenant utiliser l' useRefAPI React Hook

https://reactjs.org/docs/hooks-reference.html#useref

déclaration

let myRef = useRef()

composant

<div ref={myRef}>My Component</div>

Utilisation

window.scrollTo({ behavior: 'smooth', top: myRef.current.offsetTop })

J'essaye d'utiliser votre code. Je peux voir, à travers console.logcela, il exécute votre window.scrollTodéclaration (ajustée pour mon cas) mais pourtant elle ne défile pas. Cela pourrait-il être lié au fait que j'utilise un React Bootstrap Modal?
robertwerner_sf

9

Vous pouvez également utiliser la scrollIntoViewméthode pour faire défiler jusqu'à un élément donné.

handleScrollToElement(event) {
const tesNode = ReactDOM.findDOMNode(this.refs.test)
 if (some_logic){
  tesNode.scrollIntoView();
  }
 }

 render() {
  return (
   <div>
     <div ref="test"></div>
   </div>)
}

9

Je suis peut-être en retard à la fête, mais j'essayais de mettre en œuvre des références dynamiques à mon projet de la bonne manière et toutes les réponses que j'ai trouvées jusqu'à ce que je sache ne sont pas satisfaisantes à mon goût, alors j'ai trouvé une solution qui, je pense, est simple et utilise la manière native et recommandée de réagir pour créer la ref.

Parfois, vous trouvez que la façon dont la documentation est écrite suppose que vous avez une quantité connue de vues et dans la plupart des cas, ce nombre est inconnu, vous avez donc besoin d'un moyen de résoudre le problème dans ce cas, créez des références dynamiques pour le nombre inconnu de vues dont vous avez besoin à montrer en classe

donc la solution la plus simple à laquelle je pouvais penser et qui fonctionnait parfaitement était de faire comme suit

class YourClass extends component {

state={
 foo:"bar",
 dynamicViews:[],
 myData:[] //get some data from the web
}

inputRef = React.createRef()

componentDidMount(){
  this.createViews()
}


createViews = ()=>{
const trs=[]
for (let i = 1; i < this.state.myData.lenght; i++) {

let ref =`myrefRow ${i}`

this[ref]= React.createRef()

  const row = (
  <tr ref={this[ref]}>
<td>
  `myRow ${i}`
</td>
</tr>
)
trs.push(row)

}
this.setState({dynamicViews:trs})
}

clickHandler = ()=>{

//const scrollToView = this.inputRef.current.value
//That to select the value of the inputbox bt for demostrate the //example

value=`myrefRow ${30}`

  this[value].current.scrollIntoView({ behavior: "smooth", block: "start" });
}


render(){

return(
<div style={{display:"flex", flexDirection:"column"}}>
<Button onClick={this.clickHandler}> Search</Button>
<input ref={this.inputRef}/>
<table>
<tbody>
{this.state.dynamicViews}
<tbody>
<table>
</div>


)

}

}

export default YourClass

de cette façon, le défilement ira à la ligne que vous recherchez.

bravo et espère que cela aide les autres


8

Juil 2019 - Crochet / fonction dédié

Un hook / fonction dédié peut masquer les détails de l'implémentation et fournit une API simple à vos composants.

React 16.8 + Composant fonctionnel

const useScroll = () => {
  const htmlElRef = useRef(null)
  const executeScroll = () => window.scrollTo(0, htmlElRef.current.offsetTop)

  return [executeScroll, htmlElRef]
}

Utilisez-le dans n'importe quel composant fonctionnel.

const ScrollDemo = () => {
    const [executeScroll, htmlElRef] = useScroll()
    useEffect(executeScroll, []) // Runs after component mounts

    return <div ref={htmlElRef}>Show me</div> 
}

démo complète

Composant de classe React 16.3 +

const utilizeScroll = () => {
  const htmlElRef = React.createRef()
  const executeScroll = () => window.scrollTo(0, htmlElRef.current.offsetTop)

  return {executeScroll, htmlElRef}
}

Utilisez-le dans n'importe quel composant de classe.

class ScrollDemo extends Component {
  constructor(){
    this.elScroll = utilizeScroll()
  }

  componentDidMount(){
    this.elScroll.executeScroll()
  }

  render(){
    return <div ref={this.elScroll.htmlElRef}>Show me</div> 
  }
} 

Démo complète


7

Vous pouvez essayer de cette façon:

 handleScrollToElement = e => {
    const elementTop = this.gate.offsetTop;
    window.scrollTo(0, elementTop);
 };

 render(){
  return(
      <h2 ref={elem => (this.gate = elem)}>Payment gate</h2>
 )}

Bonne solution, bien que vous souhaitiez probablement e.offsetTop plutôt que this.gate.offsetTop, puis passez this.gate à la fonction.
KingOfHypocrites

5

Vous pouvez utiliser quelque chose comme componentDidUpdate

componentDidUpdate() {
  var elem = testNode //your ref to the element say testNode in your case; 
  elem.scrollTop = elem.scrollHeight;
};

3
Je pense que l'utilisation de l'identifiant d'élément n'est pas préférée dans react. Il brise le concept de dom virtuel
iamsaksham

L'utilisation de la méthode du cycle de vie est le moyen d'aller aussi loin que WHEN / WHERE pour exécuter le code. Mais vous voulez probablement utiliser les autres méthodologies que vous voyez dans cette réponse pour le code réel
Dameo

3

J'ai eu un scénario simple, lorsque l'utilisateur clique sur l'élément de menu dans ma barre de navigation de l'interface utilisateur matérielle, je veux les faire défiler jusqu'à la section de la page. Je pourrais utiliser des refs et les passer à travers tous les composants, mais je déteste les accessoires de threading avec plusieurs composants car cela rend le code fragile.

Je viens d'utiliser vanilla JS dans mon composant de réaction, il s'avère que cela fonctionne très bien. J'ai placé un identifiant sur l'élément vers lequel je voulais faire défiler et dans mon composant d'en-tête, je viens de le faire.

const scroll = () => {
  const section = document.querySelector( '#contact-us' );
  section.scrollIntoView( { behavior: 'smooth', block: 'start' } );
};

2

Suivez ces étapes:

1) installer:

npm install react-scroll-to --save

2) Importez le package:

import { ScrollTo } from "react-scroll-to";

3) utilisation:

class doc extends Component {
  render() {
    return(
      <ScrollTo>
        {({ scroll }) => (
          <a onClick={() => scroll({ x: 20, y: 500, , smooth: true })}>Scroll to Bottom</a>
        )}
      </ScrollTo>
    )
  }
}

2

Voici l' extrait de code du composant de classe que vous pouvez utiliser pour résoudre ce problème:

Cette approche a utilisé la référence et défile également en douceur vers la référence cible

import React, { Component } from 'react'

export default class Untitled extends Component {
  constructor(props) {
    super(props)
    this.howItWorks = React.createRef() 
  }

  scrollTohowItWorks = () =>  window.scroll({
    top: this.howItWorks.current.offsetTop,
    left: 0,
    behavior: 'smooth'
  });

  render() {
    return (
      <div>
       <button onClick={() => this.scrollTohowItWorks()}>How it works</button>
       <hr/>
       <div className="content" ref={this.howItWorks}>
         Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nesciunt placeat magnam accusantium aliquid tenetur aspernatur nobis molestias quam. Magnam libero expedita aspernatur commodi quam provident obcaecati ratione asperiores, exercitationem voluptatum!
       </div>
      </div>
    )
  }
}

2

La meilleure façon est d'utiliser element.scrollIntoView({ behavior: 'smooth' }). Cela fait défiler l'élément en vue avec une belle animation.

Lorsque vous le combinez avec React useRef(), cela peut être fait de la manière suivante.

import React, { useRef } from 'react'

const Article = () => {
  const titleRef = useRef()

  function handleBackClick() {
      titleRef.current.scrollIntoView({ behavior: 'smooth' })
  }

  return (
      <article>
            <h1 ref={titleRef}>
                A React article for Latin readers
            </h1>

            // Rest of the article's content...

            <button onClick={handleBackClick}>
                Back to the top
            </button>
        </article>
    )
}

Lorsque vous souhaitez faire défiler jusqu'à un composant React, vous devez transmettre la référence à l'élément rendu. Cet article approfondira le problème .


C'est bien mieux. Je faisais à l'origine (ref) => window.scrollTo(0, ref.current.offsetTop) mais n'obtenais qu'un petit décalage par rapport au sommet et n'atteignais pas la cible. Je crois que c'était parce que l'emplacement de l'arbitre était quelqu'un calculé au début et ensuite non mis à jour. Votre suggestion a résolu mon problème, contrairement à la réponse acceptée.
Willy

1

Ce qui a fonctionné pour moi:

class MyComponent extends Component {
    constructor(props) {
        super(props);
        this.myRef = React.createRef(); // Create a ref    
    }

    // Scroll to ref function
    scrollToMyRef = () => {
        window.scrollTo({
            top:this.myRef.offsetTop, 
            // behavior: "smooth" // optional
        });
    };

    // On component mount, scroll to ref
    componentDidMount() {
        this.scrollToMyRef();
    }

    // Render method. Note, that `div` element got `ref`.
    render() {
        return (
            <div ref={this.myRef}>My component</div>
        )
    }
}

1

Attention, je ne pouvais pas faire fonctionner ces solutions sur les composants de l'interface matérielle. On dirait qu'ils n'ont pas la currentpropriété.

Je viens d'ajouter un vide divparmi mes composants et de définir l'accessoire de référence dessus.


0
 <div onScrollCapture={() => this._onScrollEvent()}></div>

 _onScrollEvent = (e)=>{
     const top = e.nativeEvent.target.scrollTop;
     console.log(top); 
}


0

J'ai utilisé ceci dans une fonction onclick pour faire défiler en douceur jusqu'à un div où son id est "step2Div".

let offset = 100;
window.scrollTo({
    behavior: "smooth",
    top:
    document.getElementById("step2Div").getBoundingClientRect().top -
    document.body.getBoundingClientRect().top -
    offset
});
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.