¿Cómo desplazarse a un elemento?

181

Tengo un widget de chat que muestra una variedad de mensajes cada vez que me desplazo hacia arriba. El problema que estoy enfrentando ahora es que el control deslizante permanece fijo en la parte superior cuando se cargan los mensajes. Quiero que se centre en el último elemento del índice de la matriz anterior. Descubrí que puedo hacer referencias dinámicas pasando el índice, pero también necesitaría saber qué tipo de función de desplazamiento usar para lograr eso

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

  render() {

    return (
      <div>
        <div ref="test"></div>
      </div>)
  }
edmamerto
fuente
1
Para una solución integrada: npmjs.com/package/react-scroll-to-component
tokland

Respuestas:

303

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

Haga clic aquí para ver una demostración completa de StackBlits

Reaccionar 16.3 +, componente de clase

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 clase: devolución de llamada de referencia

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

No use referencias de cadena.

Los árbitros de cadena perjudican el rendimiento, no son compostables y están saliendo (agosto de 2018).

las referencias de cadena tienen algunos problemas, se consideran heredadas y es probable que se eliminen en una de las versiones futuras. [Documentación oficial de React]

recurso1 recurso2

Opcional: suavizar la animación de desplazamiento

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

Refiriéndose a un niño

Queremos que la referencia se una a un elemento dom, no a un componente de reacción. Entonces, al pasarlo a un componente hijo, no podemos nombrar la referencia de utilería.

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

Luego adjunte el accesorio de referencia a un elemento dom.

const ChildComp = (props) => {
    return <div ref={props.refProp} />
}
Ben Carp
fuente
55
window.scrollTo(0, offsetTop)es una mejor opción con mejor soporte entre los navegadores actuales
MoMo
1
Podría asegurarse de que sea coherente en su ejemplo. Comenzamos desde myRef, vamos con domRef y terminamos con tesNode?. Eso es bastante confuso
Louis Lecocq
44
Obvio después del hecho, pero es importante mencionar que esto solo funciona para elementos DOM nativos y no para cualquier componente React.
jpunk11
1
@ jpunk11 Acabo de actualizar mi respuesta. La respuesta actualizada explica cómo desplazarse a un elemento dom que está en un componente de clase secundaria.
Ben Carp
2
@SimonFranzen Eche un vistazo a mi respuesta actualizada - TLDR - caso de componentes de clase. Cuando se llama scrollToMyRef, se desplazará al elemento secundario al que adjuntó la referencia. Puede pasar el método a un componente secundario diferente y activarlo desde allí.
Ben Carp
55

esto funcionó para mí

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

EDITAR: quería ampliar esto basado en los comentarios.

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

<div ref={scrollTo}>Item</div>
chii
fuente
1
dónde poner esto
su_sundariya
en el método del ciclo de vida o constructor
su_sundariya
1
Funciona de maravilla. Nada de lo anterior no funciona para mí, ¡esta respuesta debería aceptarse!
Shin
1
Funcionó para mí, solo tenga en cuenta que 'inicio' es el valor predeterminado del parámetro 'bloque'.
Liron Lavi
Esto funcionó para mí cuando la respuesta de @Ben Carp no lo haría.
Jason Masters
37

Simplemente encuentre la posición superior del elemento que ya ha determinado https://www.w3schools.com/Jsref/prop_element_offsettop.asp y luego desplácese a esta posición a través del scrollTométodo https://www.w3schools.com/Jsref/met_win_scrollto.asp

Algo como esto debería 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>)
}

ACTUALIZAR:

desde Reaccionar v16.3 la React.createRef()se prefiere

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
fuente
2
Esta es la mejor respuesta. El uso ReactDOM.findDomNode()es una mejor práctica: dado que React vuelve a representar los componentes, un div que simplemente obtiene por su ID podría no existir para el momento en que llama a la función
Buena idea
44
De acuerdo con la documentación oficial, debe intentar evitar el uso findDOMNode. En la mayoría de los casos, puede adjuntar una referencia al nodo DOM y evitar usarla findDOMNode.
Facyo Kouch
1
Tenga en cuenta que el uso de this.refs mediante el mapeo de cadenas está en desuso, consulte: stackoverflow.com/questions/43873511/…
Himmet Avsar
1
Nota: tuve que usar en this.myRef.current.scrollIntoView()lugar de window.scrollTo(0, this.myRef).
Babbz77
14

El uso de findDOMNode será desaprobado eventualmente.

El método preferido es usar referencias de devolución de llamada.

github eslint

sww314
fuente
3
Incluya la parte relevante del material vinculado para que, en caso de que se elimine, su respuesta no sea inútil.
totymedli
12

Ahora puede usar useRefdesde reaccionar hook API

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

declaración

let myRef = useRef()

componente

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

Utilizar

window.scrollTo({ behavior: 'smooth', top: myRef.current.offsetTop })
Tanapruk Tangphianphan
fuente
Estoy tratando de usar tu código. Puedo ver, a través de console.logeso, está ejecutando su window.scrollTodeclaración (ajustada para mi caso) pero aún no se desplaza. ¿Podría esto estar relacionado con el hecho de que estoy usando un React Bootstrap Modal?
robertwerner_sf
9

También puede usar el scrollIntoViewmétodo para desplazarse a un elemento dado.

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

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

Puede que llegue tarde a la fiesta, pero estaba tratando de implementar referencias dinámicas a mi proyecto de la manera adecuada y toda la respuesta que he encontrado hasta ahora no es tranquilamente satisfactoria para mi gusto, así que se me ocurrió una solución que creo que es simple y utiliza la forma de reacción nativa y recomendada para crear la referencia.

a veces encuentra que la forma en que se escribe la documentación supone que tiene una cantidad conocida de vistas y, en la mayoría de los casos, este número es desconocido, por lo que necesita una forma de resolver el problema en este caso, crear referencias dinámicas al número desconocido de vistas que necesita para mostrar en la clase

así que la solución más simple que se me ocurrió y funcionó a la perfección fue hacer lo siguiente

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 esa manera el desplazamiento irá a la fila que esté buscando.

aplausos y espero que ayude a otros

Miguel Sedek
fuente
8

Jul 2019 - Gancho / función dedicado

Un enlace / función dedicado puede ocultar detalles de implementación y proporciona una API simple a sus componentes.

Reacciona 16.8 + componente funcional

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

  return [executeScroll, htmlElRef]
}

Úselo en cualquier componente funcional.

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

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

demo completa

Reaccionar 16.3 + componente de clase

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

  return {executeScroll, htmlElRef}
}

Úselo en cualquier componente de clase.

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

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

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

Demo completa

Ben Carp
fuente
7

Podrías intentarlo de esta manera:

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

 render(){
  return(
      <h2 ref={elem => (this.gate = elem)}>Payment gate</h2>
 )}
jirina Brezinova
fuente
Buena solución, aunque probablemente desee e.offsetTop en lugar de this.gate.offsetTop y luego pasar this.gate a la función.
KingOfHypocrites
5

Puedes usar algo como componentDidUpdate

componentDidUpdate() {
  var elem = testNode //your ref to the element say testNode in your case; 
  elem.scrollTop = elem.scrollHeight;
};
Raviteja
fuente
3
Creo que usar id de elemento no es preferido en reaccionar. Rompe el concepto virtual dom
iamsaksham
Usar el método del ciclo de vida es el camino a seguir hasta CUANDO / DÓNDE ejecutar el código. Pero probablemente quiera usar las otras metodologías que ve en esta respuesta para el código real
Dameo
3

Tuve un escenario simple: cuando el usuario hace clic en el elemento del menú en mi barra de navegación de la interfaz de usuario del material, quiero desplazarlos hacia la sección de la página. Podría usar referencias y enhebrarlas a través de todos los componentes, pero odio enhebrar accesorios de múltiples componentes porque eso hace que el código sea frágil.

Acabo de usar Vanilla JS en mi componente de reacción, resulta que funciona bien. Coloqué una identificación en el elemento al que quería desplazarme y en mi componente de encabezado acabo de hacer esto.

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

Sigue estos pasos:

1) Instalar:

npm install react-scroll-to --save

2) Importar el paquete:

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
fuente
2

Aquí está el fragmento de código de Componente de clase que puede usar para resolver este problema:

Este enfoque utiliza la referencia y también se desplaza suavemente a la referencia objetivo

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
fuente
2

La mejor manera es usar element.scrollIntoView({ behavior: 'smooth' }) . Esto desplaza el elemento a la vista con una bonita animación.

Cuando lo combina con React useRef(), se puede hacer de la siguiente manera.

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

Cuando desee desplazarse a un componente Reaccionar, debe reenviar la referencia al elemento representado. Este artículo profundizará en el problema .

robinvdvleuten
fuente
Esto es mucho mejor Originalmente estaba haciendo, (ref) => window.scrollTo(0, ref.current.offsetTop) pero luego solo obtuve un pequeño desplazamiento desde la parte superior y no llegué al objetivo. Creo que esto se debió a que la ubicación del árbitro era alguien calculada al principio y luego no actualizada. Su sugerencia solucionó mi problema mientras que la respuesta aceptada no.
Willy hace
1

Lo que funcionó para mí:

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
fuente
1

Solo un aviso, no pude hacer que estas soluciones funcionen en los componentes de la interfaz de usuario de materiales. Parece que no tienen la currentpropiedad.

Acabo de agregar un vacío diventre mis componentes y configuré el accesorio de referencia en eso.

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

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

Utilicé esto dentro de una función onclick para desplazarme suavemente a un div donde su id es "step2Div".

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