Jak przewinąć do elementu?

181

Mam widżet czatu, który wyświetla szereg wiadomości za każdym razem, gdy przewijam w górę. Problem, z którym teraz mam do czynienia, polega na tym, że suwak pozostaje na górze podczas ładowania wiadomości. Chcę, aby skupił się na ostatnim elemencie indeksu z poprzedniej tablicy. Dowiedziałem się, że mogę tworzyć dynamiczne referencje, przekazując indeks, ale musiałbym również wiedzieć, jakiego rodzaju funkcji przewijania użyć, aby to osiągnąć

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

  render() {

    return (
      <div>
        <div ref="test"></div>
      </div>)
  }
edmamerto
źródło
1
Pakietowe
tokland

Odpowiedzi:

301

React 16.8 +, komponent funkcjonalny

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

Kliknij tutaj, aby zobaczyć pełne demo na StackBlits

React 16.3 +, składnik klasy

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. 

}

Komponent klasy - odwołanie zwrotne

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

Nie używaj referencji typu String.

String refs szkodzi wydajności, nie można ich komponować i są w drodze (sierpień 2018).

string refs mają pewne problemy, są uważane za starsze i prawdopodobnie zostaną usunięte w jednym z przyszłych wydań. [Oficjalna dokumentacja React]

zasób1 zasób2

Opcjonalnie: Wygładź animację przewijania

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

Podanie referencji dziecku

Chcemy, aby ref był dołączony do elementu dom, a nie do składnika reagującego. Zatem przekazując go do komponentu potomnego, nie możemy nazwać właściwości ref.

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

Następnie dołącz element ref do elementu dom.

const ChildComp = (props) => {
    return <div ref={props.refProp} />
}
Ben Carp
źródło
5
window.scrollTo(0, offsetTop)jest lepszą opcją z lepszym wsparciem wśród obecnych przeglądarek
MoMo
1
Mógłbyś upewnić się, że jesteś konsekwentny w swoim przykładzie. Zaczynamy od myRef, przechodzimy od domRef, a kończąc na tesNode?. To dość mylące
Louis Lecocq
4
Oczywiście po fakcie, ale ważne jest, aby wspomnieć, że działa to tylko dla natywnych elementów DOM, a nie tylko dla dowolnego komponentu React.
jpunk11
1
@ jpunk11 Właśnie zaktualizowałem moją odpowiedź. Zaktualizowana odpowiedź wyjaśnia, jak przewijać do elementu dom, który znajduje się w składniku klasy podrzędnej.
Ben Carp
2
@SimonFranzen Spójrz na moją zaktualizowaną odpowiedź - TLDR - obudowa komponentu klasy. Wywołanie scrollToMyRef spowoduje przewinięcie do dziecka, do którego dołączyłeś referencję. Możesz przekazać metodę do innego składnika podrzędnego i stamtąd ją wyzwolić.
Ben Carp
55

to działało dla mnie

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

EDYCJA: Chciałem to rozwinąć na podstawie komentarzy.

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

<div ref={scrollTo}>Item</div>
chii
źródło
1
gdzie to umieścić
su_sundariya
w metodzie cyklu życia lub konstruktorze
su_sundariya
1
Działa jak marzenie. Żadne z powyższych nie działa dla mnie, to powinna zostać zaakceptowana odpowiedź!
Shin,
1
U mnie zadziałało, po prostu zauważ, że „start” jest domyślną wartością parametru „blok”.
Liron Lavi
To zadziałało dla mnie, gdy odpowiedź @Ben Carp nie.
Jason Masters
37

Po prostu znajdź najwyższą pozycję elementu, który już ustaliłeś https://www.w3schools.com/Jsref/prop_element_offsettop.asp, a następnie przewiń do tej pozycji scrollTometodą https://www.w3schools.com/Jsref/met_win_scrollto.asp

Coś takiego powinno działać:

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

AKTUALIZACJA:

od React v16.3React.createRef() jest korzystne

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
źródło
2
To jest lepsza odpowiedź. Używanie ReactDOM.findDomNode()jest lepszą praktyką - ponieważ React ponownie renderuje komponenty, div, który otrzymujesz po prostu za pomocą jego identyfikatora, może nie istnieć do czasu wywołania funkcji
Dobry pomysł
4
Zgodnie z oficjalną dokumentacją powinieneś unikać używania findDOMNode. W większości przypadków możesz dołączyć odwołanie do węzła DOM i findDOMNodew ogóle nie używać .
Facyo Kouch
1
Zauważ, że używanie this.refs przez mapowanie ciągów jest przestarzałe, zobacz: stackoverflow.com/questions/43873511/ ...
Himmet Avsar
1
Uwaga: musiałem użyć this.myRef.current.scrollIntoView()zamiast window.scrollTo(0, this.myRef).
Babbz77
14

Korzystanie z findDOMNode będzie ostatecznie przestarzałe.

Preferowaną metodą jest użycie odwołań zwrotnych.

github eslint

sww314
źródło
3
Uwzględnij odpowiednią część połączonego materiału, aby w przypadku usunięcia odpowiedzi Twoja odpowiedź nie stała się bezużyteczna.
totymedli
12

Możesz teraz używać useRefinterfejsu API z funkcji react hook

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

deklaracja

let myRef = useRef()

składnik

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

Posługiwać się

window.scrollTo({ behavior: 'smooth', top: myRef.current.offsetTop })
Tanapruk Tangphianphan
źródło
Próbuję użyć twojego kodu. Widzę, przez console.logto, że wykonuje twoje window.scrollTopolecenie (dostosowane do mojego przypadku), ale mimo to się nie przewija. Czy może to być związane z faktem, że używam modalu React Bootstrap?
robertwerner_sf
9

Możesz również użyć scrollIntoViewmetody, aby przejść do danego elementu.

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

 render() {
  return (
   <div>
     <div ref="test"></div>
   </div>)
}
Farshad J.
źródło
9

Mogę się spóźnić na imprezę, ale starałem się zaimplementować dynamiczne odniesienia do mojego projektu we właściwy sposób, a wszystkie odpowiedzi, które znalazłem, dopóki nie wiem, nie są spokojne, satysfakcjonujące w moim guście, więc wymyśliłem rozwiązanie, które moim zdaniem jest prosty i używa natywnego i zalecanego sposobu reagowania, aby utworzyć ref.

czasami okazuje się, że sposób pisania dokumentacji zakłada, że ​​masz znaną liczbę widoków iw większości przypadków ta liczba jest nieznana, więc potrzebujesz sposobu na rozwiązanie problemu w tym przypadku, utwórz dynamiczne odniesienia do nieznanej liczby widoków, których potrzebujesz pokazać w klasie

więc najprostszym rozwiązaniem, jakie mogłem wymyślić i działałem bezbłędnie, było wykonanie następujących czynności

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

w ten sposób przewiń przejdzie do dowolnego wiersza, którego szukasz.

okrzyki i mam nadzieję, że pomoże innym

Miguel Sedek
źródło
8

Lipiec 2019 - Dedykowany haczyk / funkcja

Dedykowany punkt zaczepienia / funkcja może ukrywać szczegóły implementacji i zapewnia proste API dla twoich komponentów.

React 16.8 + Komponent funkcjonalny

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

  return [executeScroll, htmlElRef]
}

Użyj go w dowolnym funkcjonalnym komponencie.

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

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

pełne demo

Reaguj Komponent klasy 16.3 +

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

  return {executeScroll, htmlElRef}
}

Użyj go w dowolnym komponencie klasy.

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

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

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

Pełne demo

Ben Carp
źródło
7

Możesz spróbować w ten sposób:

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

 render(){
  return(
      <h2 ref={elem => (this.gate = elem)}>Payment gate</h2>
 )}
jirina Brezinova
źródło
Dobre rozwiązanie, chociaż prawdopodobnie wolisz e.offsetTop zamiast this.gate.offsetTop, a następnie przekaż this.gate do funkcji.
KingOfHypocrites
5

Możesz użyć czegoś takiego jak componentDidUpdate

componentDidUpdate() {
  var elem = testNode //your ref to the element say testNode in your case; 
  elem.scrollTop = elem.scrollHeight;
};
Raviteja
źródło
3
Myślę, że użycie identyfikatora elementu nie jest preferowane w reakcji.
Łamie
Korzystanie z metody cyklu życia jest sposobem na osiągnięcie tego, KIEDY / GDZIE uruchomić kod. Ale prawdopodobnie chcesz użyć innych metodologii, które widzisz w tej odpowiedzi, dla rzeczywistego kodu
Dameo,
3

Miałem prosty scenariusz, kiedy użytkownik kliknie element menu na moim pasku nawigacyjnym interfejsu użytkownika materiałów, chcę przewinąć je w dół do sekcji na stronie. Mógłbym użyć refs i przeplatać je przez wszystkie komponenty, ale nienawidzę wątkowania rekwizytów wielu komponentów, ponieważ to sprawia, że ​​kod jest kruchy.

Właśnie użyłem vanilla JS w moim komponencie React, okazuje się, że działa dobrze. Umieściłem identyfikator na elemencie, do którego chciałem przewinąć, iw komponencie nagłówka właśnie to zrobiłem.

const scroll = () => {
  const section = document.querySelector( '#contact-us' );
  section.scrollIntoView( { behavior: 'smooth', block: 'start' } );
};
Jose
źródło
2

Wykonaj następujące kroki:

1) Zainstaluj:

npm install react-scroll-to --save

2) Zaimportuj pakiet:

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

3) Użycie:

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

Oto fragment kodu komponentu klasy, którego możesz użyć do rozwiązania tego problemu:

To podejście wykorzystywało odniesienie, a także płynnie przewijało do odniesienia docelowego

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
źródło
1

Co mi pomogło:

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
źródło
1

Uwaga, nie mogłem zmusić tych rozwiązań do pracy z komponentami Material UI. Wygląda na to, że nie mającurrent nieruchomości.

Właśnie dodałem pusty divelement wśród moich komponentów i ustawiłem na nim atrybut ref.

Steve
źródło
0
 <div onScrollCapture={() => this._onScrollEvent()}></div>

 _onScrollEvent = (e)=>{
     const top = e.nativeEvent.target.scrollTop;
     console.log(top); 
}
bambus
źródło
0

Użyłem tego wewnątrz funkcji onclick, aby płynnie przewijać do elementu div, którego identyfikator to „step2Div”.

let offset = 100;
window.scrollTo({
    behavior: "smooth",
    top:
    document.getElementById("step2Div").getBoundingClientRect().top -
    document.body.getBoundingClientRect().top -
    offset
});
Suneth Thotagamuwa
źródło
0

Najmilszym sposobem jest użycie element.scrollIntoView({ behavior: 'smooth' }). Spowoduje to przewinięcie elementu do widoku z ładną animacją.

Kiedy połączysz to z Reactem useRef(), możesz to zrobić w następujący sposób.

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

Jeśli chcesz przewinąć do komponentu React, musisz przekazać ref do renderowanego elementu. W tym artykule szczegółowo omówimy problem .

robinvdvleuten
źródło