Como rolar para um elemento?

181

Eu tenho um widget de bate-papo que exibe uma variedade de mensagens toda vez que eu rolar para cima. O problema que estou enfrentando agora é que o controle deslizante permanece fixo na parte superior quando as mensagens são carregadas. Eu quero que ele se concentre no último elemento de índice da matriz anterior. Eu descobri que posso fazer referências dinâmicas passando o índice, mas também precisaria saber que tipo de função de rolagem usar para conseguir isso.

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

  render() {

    return (
      <div>
        <div ref="test"></div>
      </div>)
  }
edmamerto
fonte
1
Para uma solução em pacote configurável
tokland

Respostas:

302

Reagir 16,8 +, componente funcional

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

Clique aqui para obter uma demonstração completa do StackBlits

Reagir 16.3 +, componente 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. 

}

Componente de classe - retorno de chamada de referência

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ão use String refs.

Refs de string prejudicam o desempenho, não são compostáveis ​​e estão saindo (agosto de 2018).

refs de string têm alguns problemas, são considerados herdados e provavelmente serão removidos em uma das versões futuras. [Documentação oficial do Reagir]

resource1 resource2

Opcional: Suavizar animação de rolagem

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

Passando ref para uma criança

Queremos que o ref seja anexado a um elemento dom, não a um componente de reação. Portanto, ao passá-lo para um componente filho, não podemos nomear o prop ref.

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

Em seguida, conecte o ref prop a um elemento dom.

const ChildComp = (props) => {
    return <div ref={props.refProp} />
}
Ben Carp
fonte
5
window.scrollTo(0, offsetTop)é uma opção melhor com melhor suporte entre os navegadores atuais
MoMo
1
Pode garantir que você seja consistente em seu exemplo. Estamos começando com myRef, indo com domRef e terminando com tesNode?. Isso é bastante confuso
Louis Lecocq
4
Óbvio após o fato, mas é importante mencionar que isso funciona apenas para elementos DOM nativos e não apenas para qualquer componente React.
jpunk11
1
@ jpunk11 Acabei de atualizar minha resposta. A resposta atualizada explica como rolar para um elemento dom que está em um componente de classe filho.
Ben Carp
2
@SimonFranzen Dê uma olhada na minha resposta atualizada - caso do componente TLDR - classe. Quando scrollToMyRef é chamado, ele rolará para o filho ao qual você anexou a referência. Você pode passar o método para um componente filho diferente e acioná-lo a partir daí.
Ben Carp
55

isso funcionou para mim

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

EDIT: Eu queria expandir isso com base nos comentários.

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

<div ref={scrollTo}>Item</div>
chii
fonte
1
onde colocar isso
su_sundariya
no método ou construtor do ciclo de vida
su_sundariya 25/10
1
Funciona como um encanto. Nenhuma das opções acima não funciona para mim, essa resposta deve ser aceita!
Shin
1
Trabalhou para mim, observe que 'start' é o valor padrão do parâmetro 'block'.
Liron Lavi
Isso funcionou para mim quando a resposta de Ben Carp não.
Jason Masters
37

Basta encontrar a posição superior do elemento que você já determinou https://www.w3schools.com/Jsref/prop_element_offsettop.asp e, em seguida, role para esta posição através do scrollTométodo https://www.w3schools.com/Jsref/met_win_scrollto.asp

Algo assim deve funcionar:

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

ATUALIZAR:

desde Reagir v16.3 o React.createRef()é preferido

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>)
}
Roman Maksimov
fonte
2
Essa é a melhor resposta. Usando ReactDOM.findDomNode()é melhor prática - desde Reagir re-processa componentes, uma div que você simplesmente obter pelo seu ID pode não existir no momento em que você chamar a função
Boa Ideia
4
De acordo com a documentação oficial, você deve evitar o uso findDOMNode. Na maioria dos casos, você pode anexar uma referência ao nó DOM e evitar o uso findDOMNode.
você precisa saber é o seguinte
1
Observe que o uso de this.refs pelo mapeamento de string foi descontinuado, consulte: stackoverflow.com/questions/43873511/…
Himmet Avsar
1
Nota: eu tive que usar em this.myRef.current.scrollIntoView()vez de window.scrollTo(0, this.myRef).
Babbz77
14

O uso de findDOMNode será descontinuado eventualmente.

O método preferido é usar refs de retorno de chamada.

github eslint

sww314
fonte
3
Inclua a parte relevante do material vinculado para que, caso seja removida, sua resposta não se torne inútil.
1011818
12

Agora você pode usar a useRefpartir da API de gancho de reação

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

declaração

let myRef = useRef()

componente

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

Usar

window.scrollTo({ behavior: 'smooth', top: myRef.current.offsetTop })
Tanapruk Tangphianphan
fonte
Estou tentando usar seu código. Eu posso ver, através console.logdisso, que está executando sua window.scrollToinstrução (ajustada para o meu caso), mas ainda assim não rola. Isso pode estar relacionado ao fato de eu estar usando um React Bootstrap Modal?
Robertwerner_sf 17/10/19
9

Você também pode usar o scrollIntoViewmétodo para rolar para um determinado elemento.

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

 render() {
  return (
   <div>
     <div ref="test"></div>
   </div>)
}
Farshad J
fonte
9

Talvez eu esteja atrasado para a festa, mas estava tentando implementar referências dinâmicas para o meu projeto da maneira correta e todas as respostas que encontrei até saberem que não são silenciosas, satisfazendo ao meu gosto, por isso vim com uma solução que acho que é simples e usa a maneira nativa e recomendada de reagir para criar a referência.

Às vezes, você descobre que a maneira como a documentação é gravada assume que você possui uma quantidade conhecida de visualizações e, na maioria dos casos, esse número é desconhecido; portanto, você precisa de uma maneira de resolver o problema. Nesse caso, crie referências dinâmicas para o número desconhecido de visualizações necessárias. para mostrar na aula

então a solução mais simples que eu consegui pensar e funcionou perfeitamente foi fazer o seguinte

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

Dessa forma, o pergaminho irá para qualquer linha que você estiver procurando ..

aplausos e espero que ajude os outros

Miguel Sedek
fonte
8

Jul 2019 - Gancho / função dedicada

Um gancho / função dedicado pode ocultar os detalhes da implementação e fornece uma API simples para seus componentes.

Reagir 16.8 + Componente Funcional

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

  return [executeScroll, htmlElRef]
}

Use-o em qualquer componente funcional.

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

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

demonstração completa

Componente React 16.3 + class

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

  return {executeScroll, htmlElRef}
}

Use-o em qualquer componente de classe.

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

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

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

Demonstração completa

Ben Carp
fonte
7

Você pode tentar desta maneira:

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

 render(){
  return(
      <h2 ref={elem => (this.gate = elem)}>Payment gate</h2>
 )}
jirina Brezinova
fonte
Boa solução, embora você provavelmente deseje e.offsetTop em vez de this.gate.offsetTop e depois passe this.gate para a função
KingOfHypocrites
5

Você pode usar algo como componentDidUpdate

componentDidUpdate() {
  var elem = testNode //your ref to the element say testNode in your case; 
  elem.scrollTop = elem.scrollHeight;
};
Raviteja
fonte
3
Eu acho que usar o elemento id não é preferido em reagir. Ele quebra o conceito dom virtual
iamsaksham
Usar o método do ciclo de vida é o caminho a percorrer até WHEN / WHERE para executar o código. Mas provavelmente deseja usar as outras metodologias que você vê nesta resposta para o código real #
Dameo 20/07/18
3

Eu tive um cenário simples: quando o usuário clica no item de menu da minha barra de navegação da interface do usuário de material, quero rolar para baixo até a seção da página. Eu poderia usar refs e enfiá-los por todos os componentes, mas eu odeio threading props props múltiplos componentes porque isso torna o código frágil.

Acabei de usar o vanilla JS no meu componente de reação, e funciona bem. Coloquei um ID no elemento que eu queria rolar para e no componente do cabeçalho, fiz isso.

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

Siga esses passos:

1) Instale:

npm install react-scroll-to --save

2) Importe o pacote:

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

3) Uso:

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

Aqui está o trecho de código do componente de classe que você pode usar para resolver esse problema:

Essa abordagem usou a ref e também rola suavemente até a ref de destino

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>
    )
  }
}
bello hargbola
fonte
2

A melhor maneira é usar element.scrollIntoView({ behavior: 'smooth' }). Isso rola o elemento em exibição com uma bela animação.

Quando você o combina com o React useRef(), isso pode ser feito da seguinte maneira.

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

Quando você deseja rolar para um componente React, precisa encaminhar a ref para o elemento renderizado. Este artigo irá aprofundar o problema .

robinvdvleuten
fonte
Assim é bem melhor. Inicialmente, eu estava fazendo, (ref) => window.scrollTo(0, ref.current.offsetTop) mas apenas obtendo um pequeno deslocamento do topo e não chegando ao alvo. Acredito que isso ocorreu porque a localização do juiz era alguém calculado no começo e depois não atualizado. Sua sugestão resolveu meu problema, enquanto a resposta aceita não.
Willy
1

O que funcionou para mim:

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>
        )
    }
}
Artur Barseghyan
fonte
1

Apenas um aviso, não consegui que essas soluções funcionassem nos componentes da interface do usuário do Material. Parece que eles não têm a currentpropriedade.

Acabei de adicionar um vazio diventre meus componentes e definir o suporte de referência sobre isso.

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

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

Eu usei isso dentro de uma função onclick para rolar suavemente para uma div onde seu ID é "step2Div".

let offset = 100;
window.scrollTo({
    behavior: "smooth",
    top:
    document.getElementById("step2Div").getBoundingClientRect().top -
    document.body.getBoundingClientRect().top -
    offset
});
Suneth Thotagamuwa
fonte