Как прокрутить до элемента?

181

У меня есть виджет чата, который выдает массив сообщений каждый раз, когда я прокручиваю вверх. Проблема, с которой я сейчас сталкиваюсь, заключается в том, что ползунок остается зафиксированным сверху при загрузке сообщений. Я хочу сосредоточиться на последнем элементе индекса из предыдущего массива. Я понял, что могу делать динамические ссылки, передавая индекс, но мне также нужно знать, какую функцию прокрутки использовать для достижения этой цели.

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

  render() {

    return (
      <div>
        <div ref="test"></div>
      </div>)
  }
edmamerto
источник
1
Для комплексного решения: npmjs.com/package/react-scroll-to-component
tokland

Ответы:

303

Реакт 16.8+, Функциональный компонент

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

Нажмите здесь для полной демонстрации по StackBlits

Реагировать 16.3 +, Класс компонента

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. 

}

Компонент класса - Ref обратный вызов

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

Не используйте строковые ссылки.

Строковые ссылки наносят вред производительности, не поддаются компоновке и находятся на выходе (август 2018 г.).

Строковые ссылки имеют некоторые проблемы, считаются устаревшими и, вероятно, будут удалены в одном из будущих выпусков. [Официальная документация React]

ресурс1 ресурс2

Необязательно: сгладить анимацию прокрутки

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

Передача ссылки ребенку

Мы хотим, чтобы ссылка была присоединена к элементу dom, а не к компоненту реакции. Таким образом, передавая его дочернему компоненту, мы не можем назвать опорный реф.

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

Затем прикрепите опору к элементу dom.

const ChildComp = (props) => {
    return <div ref={props.refProp} />
}
Бен Карп
источник
5
window.scrollTo(0, offsetTop)лучший вариант с лучшей поддержкой среди существующих браузеров
MoMo
1
Мог бы убедиться, что вы последовательны в своем примере. Мы начинаем с myRef, переходим к domRef и заканчиваем tesNode? Это довольно странно
Луи Лекок
4
Это очевидно после факта, но важно отметить, что это работает только для собственных элементов DOM, а не только для любого компонента React.
jpunk11
1
@ jpunk11 Я только что обновил свой ответ. В обновленном ответе объясняется, как перейти к элементу dom, который находится в компоненте дочернего класса.
Бен Карп
2
@SimonFranzen Взгляните на мой обновленный вариант ответа TLDR - класс компонентов. Когда вызывается scrollToMyRef, он переходит к дочернему элементу, к которому вы прикрепили ссылку. Вы можете передать метод другому дочернему компоненту и запустить его оттуда.
Бен Карп
55

это сработало для меня

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

РЕДАКТИРОВАТЬ: Я хотел бы расширить это на основе комментариев.

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

<div ref={scrollTo}>Item</div>
чий
источник
1
где поставить это
su_sundariya
в жизненном цикле метод или конструктор
su_sundariya
1
Работает как шарм. Ничто из вышеперечисленного не работает для меня, это должен быть принят ответ!
Шин
1
Сработало для меня, просто обратите внимание, что «start» является значением по умолчанию параметра «block».
Лирон Лави
Это сработало для меня, когда ответ @Ben Carp не сработал.
Джейсон Мастерс
37

Просто найдите верхнюю позицию элемента, который вы уже определили https://www.w3schools.com/Jsref/prop_element_offsettop.asp, затем перейдите к этой позиции с помощью scrollToметода https://www.w3schools.com/Jsref/met_win_scrollto.asp

Примерно так должно работать:

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

ОБНОВИТЬ:

так React v16.3React.createRef() является предпочтительным

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
Это лучший ответ. Использование ReactDOM.findDomNode()- лучшая практика - поскольку React выполняет рендеринг компонентов, элемент div, который вы просто получаете по его идентификатору, может не существовать к тому времени, когда вы вызываете функцию
Хорошая идея
4
Согласно официальной документации, вам следует избегать использования findDOMNode. В большинстве случаев вы можете прикрепить ref к узлу DOM и findDOMNodeвообще не использовать его .
Facyo Kouch
1
Обратите внимание, что использование this.refs для сопоставления строк устарело, см .: stackoverflow.com/questions/43873511/…
Himmet Avsar
1
Примечание: я должен был использовать this.myRef.current.scrollIntoView()вместо window.scrollTo(0, this.myRef).
Babbz77
14

Использование findDOMNode в конечном итоге будет устаревшим.

Предпочтительным методом является использование обратных ссылок.

Github Eslint

sww314
источник
3
Пожалуйста, включите соответствующую часть связанного материала, чтобы в случае удаления ваш ответ не стал бесполезным.
Тотимедли
12

Теперь вы можете использовать useRef API реагирования хука

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

декларация

let myRef = useRef()

составная часть

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

использование

window.scrollTo({ behavior: 'smooth', top: myRef.current.offsetTop })
Танапрук Тангфианфан
источник
Я пытаюсь использовать твой код. Я вижу, console.logчто он выполняет ваше window.scrollToзаявление (с учетом моего случая), но пока не прокручивает. Может ли это быть связано с тем, что я использую модал React Bootstrap?
robertwerner_sf
9

Вы также можете использовать scrollIntoViewметод для прокрутки к данному элементу.

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

 render() {
  return (
   <div>
     <div ref="test"></div>
   </div>)
}
Фаршад Дж
источник
9

Возможно, я опоздал на вечеринку, но я пытался правильно реализовать динамические ссылки в своем проекте, и все ответы, которые я нашел до тех пор, пока не знали, не удовлетворяют меня по вкусу, поэтому я нашел решение, которое, мне кажется, простой и использует родной и рекомендуемый способ реагирования для создания ссылки.

иногда вы обнаруживаете, что при написании документации предполагается, что у вас есть известное количество просмотров, и в большинстве случаев это число неизвестно, поэтому в этом случае вам нужен способ решения проблемы, создайте динамические ссылки на неизвестное количество просмотров, которое вам нужно показать в классе

поэтому самое простое решение, которое я мог придумать и работать безупречно, заключалось в следующем

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

таким образом, свиток перейдет в любую строку, которую вы ищете ..

ура и надеюсь, что это помогает другим

Мигель Седек
источник
8

Июль 2019 - выделенный крюк / функция

Выделенная ловушка / функция может скрывать детали реализации и предоставляет простой API для ваших компонентов.

Реакция 16.8 + функциональный компонент

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

  return [executeScroll, htmlElRef]
}

Используйте его в любом функциональном компоненте.

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

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

полное демо

Реагировать 16,3 + класс Компонент

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

  return {executeScroll, htmlElRef}
}

Используйте его в любом компоненте класса.

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

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

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

Полная демонстрация

Бен Карп
источник
7

Вы можете попробовать так:

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

 render(){
  return(
      <h2 ref={elem => (this.gate = elem)}>Payment gate</h2>
 )}
ирина брезинова
источник
Хорошее решение, хотя вы, вероятно, хотите e.offsetTop, а не this.gate.offsetTop, а затем передать this.gate функции.
KingOfHypocrites
5

Вы можете использовать что-то вроде componentDidUpdate

componentDidUpdate() {
  var elem = testNode //your ref to the element say testNode in your case; 
  elem.scrollTop = elem.scrollHeight;
};
Raviteja
источник
3
я думаю, что использование идентификатора элемента не является предпочтительным в реакции. Это нарушает концепцию виртуального
дома
Использование метода жизненного цикла - это путь до WHEN / WHERE для запуска кода. Но, возможно, вы захотите использовать другие методологии, которые вы видите в этом ответе, для реального кода
Dameo
3

У меня был простой сценарий: когда пользователь нажимает на элемент меню в моей панели навигации пользовательского интерфейса для материалов, я хочу прокрутить их вниз до раздела на странице. Я мог бы использовать ссылки и пропустить их через все компоненты, но я терпеть не могу многопоточные реквизиты, потому что это делает код хрупким.

Я просто использовал vanilla JS в своем реактивном компоненте, оказалось, что он работает просто отлично. Поместил идентификатор в элемент, который я хотел прокрутить, и в своем компоненте заголовка я только что сделал это.

const scroll = () => {
  const section = document.querySelector( '#contact-us' );
  section.scrollIntoView( { behavior: 'smooth', block: 'start' } );
};
Хосе
источник
2

Следуй этим шагам:

1) Установите:

npm install react-scroll-to --save

2) импортировать пакет:

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

3) Использование:

class doc extends Component {
  render() {
    return(
      <ScrollTo>
        {({ scroll }) => (
          <a onClick={() => scroll({ x: 20, y: 500, , smooth: true })}>Scroll to Bottom</a>
        )}
      </ScrollTo>
    )
  }
}
Захид Али
источник
2

Вот фрагмент кода компонента класса, который можно использовать для решения этой проблемы:

Этот подход использовал ссылку, а также плавно прокручивается к целевой ссылке.

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
источник
2

Самый хороший способ - это использовать element.scrollIntoView({ behavior: 'smooth' }) . Это прокручивает элемент в поле зрения с хорошей анимацией.

Когда вы комбинируете его с React useRef(), это можно сделать следующим образом.

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

Когда вы хотите перейти к компоненту React, вам нужно переслать ссылку на визуализированный элемент. Эта статья углубится в проблему .

robinvdvleuten
источник
Это намного лучше. Первоначально я делал это, (ref) => window.scrollTo(0, ref.current.offsetTop) но потом получил небольшое смещение от вершины и не достиг цели. Я полагаю, что это произошло потому, что местоположение рефери было рассчитано в начале, а затем не обновлено. Ваше предложение решило мою проблему, а принятый ответ - нет.
Вилли
1

Что сработало для меня:

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

Просто наперед, я не мог заставить эти решения работать с компонентами пользовательского интерфейса. Похоже, у них нет currentсобственности.

Я просто добавил пустую divсреди моих компонентов и установил опорную ссылку на это.

Стив
источник
0
 <div onScrollCapture={() => this._onScrollEvent()}></div>

 _onScrollEvent = (e)=>{
     const top = e.nativeEvent.target.scrollTop;
     console.log(top); 
}
ibamboo
источник
0

Я использовал это внутри функции onclick для плавной прокрутки до div, где его id - "step2Div".

let offset = 100;
window.scrollTo({
    behavior: "smooth",
    top:
    document.getElementById("step2Div").getBoundingClientRect().top -
    document.body.getBoundingClientRect().top -
    offset
});
Сунет Тотагамува
источник