Подтвердить что ты не робот

Как я могу прокрутить div, чтобы быть видимым в ReactJS?

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

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

Каков правильный способ в React для автоматического прокрутки дочернего элемента div в представлении?

4b9b3361

Ответ 1

Я предполагаю, что у вас есть какой-то компонент List и какой-то компонент Item. То, как я это сделал в одном проекте, должен был сообщить этому элементу, был ли он активным или нет; элемент попросит список прокрутить его в поле зрения, если это необходимо. Рассмотрим следующий псевдокод:

class List extends React.Component {
  render() {
    return <div>{this.props.items.map(this.renderItem)}</div>;
  }

  renderItem(item) {
    return <Item key={item.id} item={item}
                 active={item.id === this.props.activeId}
                 scrollIntoView={this.scrollElementIntoViewIfNeeded} />
  }

  scrollElementIntoViewIfNeeded(domNode) {
    var containerDomNode = React.findDOMNode(this);
    // Determine if `domNode` fully fits inside `containerDomNode`.
    // If not, set the container scrollTop appropriately.
  }
}

class Item extends React.Component {
  render() {
    return <div>something...</div>;
  }

  componentDidMount() {
    this.ensureVisible();
  }

  componentDidUpdate() {
    this.ensureVisible();
  }

  ensureVisible() {
    if (this.props.active) {
      this.props.scrollIntoView(React.findDOMNode(this));
    }
  }
}

Лучшее решение, вероятно, должно сделать список, ответственный за прокрутку элемента в представлении (без того, чтобы элемент знал, что он даже в списке). Для этого вы можете добавить атрибут ref к определенному элементу и найти его с этим:

class List extends React.Component {
  render() {
    return <div>{this.props.items.map(this.renderItem)}</div>;
  }

  renderItem(item) {
    var active = item.id === this.props.activeId;
    var props = {
      key: item.id,
      item: item,
      active: active
    };
    if (active) {
      props.ref = "activeItem";
    }
    return <Item {...props} />
  }

  componentDidUpdate(prevProps) {
    // only scroll into view if the active item changed last render
    if (this.props.activeId !== prevProps.activeId) {
      this.ensureActiveItemVisible();
    }
  }

  ensureActiveItemVisible() {
    var itemComponent = this.refs.activeItem;
    if (itemComponent) {
      var domNode = React.findDOMNode(itemComponent);
      this.scrollElementIntoViewIfNeeded(domNode);
    }
  }

  scrollElementIntoViewIfNeeded(domNode) {
    var containerDomNode = React.findDOMNode(this);
    // Determine if `domNode` fully fits inside `containerDomNode`.
    // If not, set the container scrollTop appropriately.
  }
}

Если вы не хотите выполнять математику, чтобы определить, является ли элемент видимым внутри списка node, вы можете использовать метод DOM scrollIntoView() или специфический для Webkit scrollIntoViewIfNeeded, который имеет polyfill доступный, поэтому вы можете использовать его в браузерах, отличных от Webkit.

Ответ 2

Чтобы построить ответ на @Michelle Tilley, я иногда хочу прокручивать, если изменяется пользовательский выбор, поэтому я запускаю прокрутку на componentDidUpdate. Я также немного вычислил, как далеко прокручивается и нужна ли прокрутка, которая для меня выглядит следующим образом:

  componentDidUpdate() {
    let panel, node;
    if (this.refs.selectedSection && this.refs.selectedItem) {
      // This is the container you want to scroll.          
      panel = this.refs.listPanel;
      // This is the element you want to make visible w/i the container
      // Note: You can nest refs here if you want an item w/i the selected item          
      node = ReactDOM.findDOMNode(this.refs.selectedItem);
    }

    if (panel && node &&
      (node.offsetTop > panel.scrollTop + panel.offsetHeight || node.offsetTop < panel.scrollTop)) {
      panel.scrollTop = node.offsetTop - panel.offsetTop;
    }
  }

Ответ 3

Другой пример, который использует функцию ref, а не строку

class List extends React.Component {
  constructor(props) {
    super(props);
    this.state = { items:[], index: 0 };
    this._nodes = new Map();

    this.handleAdd = this.handleAdd.bind(this);
    this.handleRemove = this.handleRemove.bind(this);
   }

  handleAdd() {
    let startNumber = 0;
    if (this.state.items.length) {
      startNumber = this.state.items[this.state.items.length - 1];
    }

    let newItems = this.state.items.splice(0);
    for (let i = startNumber; i < startNumber + 100; i++) {
      newItems.push(i);
    }

    this.setState({ items: newItems });
  }

  handleRemove() {
    this.setState({ items: this.state.items.slice(1) });
  }

  handleShow(i) {
    this.setState({index: i});
    const node = this._nodes.get(i);
    console.log(this._nodes);
    if (node) {
      ReactDOM.findDOMNode(node).scrollIntoView({block: 'end', behavior: 'smooth'});
    }
  }

  render() {
    return(
      <div>
        <ul>{this.state.items.map((item, i) => (<Item key={i} ref={(element) => this._nodes.set(i, element)}>{item}</Item>))}</ul>
        <button onClick={this.handleShow.bind(this, 0)}>0</button>
        <button onClick={this.handleShow.bind(this, 50)}>50</button>
        <button onClick={this.handleShow.bind(this, 99)}>99</button>
        <button onClick={this.handleAdd}>Add</button>
        <button onClick={this.handleRemove}>Remove</button>
        {this.state.index}
      </div>
    );
  }
}

class Item extends React.Component
{
  render() {
    return (<li ref={ element => this.listItem = element }>
      {this.props.children}
    </li>);
  }
}

Демо: https://codepen.io/anon/pen/XpqJVe

Ответ 4

Для React 16 правильный ответ отличается от более ранних ответов:

class Something extends Component {
  constructor(props) {
    super(props);
    this.boxRef = React.createRef();
  }

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

Затем, чтобы прокрутить, просто добавьте (после конструктора):

componentDidMount() {
  if (this.props.active) { // whatever your test might be
    this.boxRef.current.scrollIntoView();
  }
}

Примечание. Вы должны использовать ".current", и вы можете отправлять параметры scrollIntoView:

scrollIntoView({
  behavior: 'smooth',
  block: 'center',
  inline: 'center',
});

(Найдено по адресу: http://www.albertgao.xyz/2018/06/07/scroll-a-not-in-view-component-into-the-view-using-react/)

Читая спецификацию, было немного сложно понять значение блока и встроенного, но после игры с ним я обнаружил, что для вертикального списка прокрутки блок: "конец" убедился, что элемент был виден без искусственной прокрутки верхней части моего контента из окна просмотра. С "центром" элемент возле дна будет сдвинут слишком далеко, и под ним появится пустое пространство. Но мой контейнер является родителем flex с оправданием: "растянуть", чтобы он мог повлиять на поведение. Я не копал слишком много. Элементы с скрытым переполнением влияют на то, как действует scrollIntoView, поэтому вам, вероятно, придется экспериментировать самостоятельно.

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

Ответ 5

В обработчике keyup/down вам просто нужно установить свойство scrollTop для div, который вы хотите прокрутить, чтобы прокрутить вниз (или вверх).

Например:

JSX:

<div ref="foo">{content}</div>

keyup/down handler:

this.refs.foo.getDOMNode().scrollTop += 10

Если вы делаете что-то подобное выше, ваш div будет прокручиваться вниз на 10 пикселей (при условии, что для div установлено значение переполнения auto или scroll в css, и ваше содержимое, конечно, переполнено).

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

Посмотрите определения MDN scrollTop и offsetTop здесь:

https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTop

https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetTop

Ответ 6

На всякий случай кто-то спотыкается здесь, я сделал это так

  componentDidMount(){
    const node = this.refs.trackerRef;
    node && node.scrollIntoView({block: "end", behavior: 'smooth'})
  }
  componentDidUpdate() {
    const node = this.refs.trackerRef;
    node && node.scrollIntoView({block: "end", behavior: 'smooth'})
  }

  render() {
    return (

      <div>
        {messages.map((msg, index) => {
          return (
            <Message key={index} msgObj={msg}
              {/*<p>some test text</p>*/}
            </Message>
          )
        })}

        <div style={{height: '30px'}} id='#tracker' ref="trackerRef"></div>
      </div>
    )
  }

scrollIntoView - это родная ссылка на функцию DOM

Он всегда будет показывать tracker div

Ответ 7

Я просто добавляю еще немного информации для других, которые ищут возможность Scroll-To в React. Я связал несколько библиотек для выполнения Scroll-To для моего приложения, и никто из них не работал из моего прецедента до тех пор, пока не обнаружил реакцию scrollchor, поэтому я решил передать его. https://github.com/bySabi/react-scrollchor

Ответ 8

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

 <NavLink onClick={() => this.scrollToHref('plans')}>Our Plans</NavLink>
  scrollToHref = (element) =>{
    let node;
    if(element === 'how'){
      node = ReactDom.findDOMNode(this.refs.how);
      console.log(this.refs)
    }else  if(element === 'plans'){
      node = ReactDom.findDOMNode(this.refs.plans);
    }else  if(element === 'about'){
      node = ReactDom.findDOMNode(this.refs.about);
    }

    node.scrollIntoView({block: 'start', behavior: 'smooth'});

  }

Затем я даю компонент, который я хотел прокрутить до ссылки, как это

<Investments ref="plans"/>