web-dev-qa-db-ja.com

Reactでオーバーフローするdivの一番下までスクロールします

max-heightoverflow-y: autoを持つulがあります。

ユーザーがli要素を十分に入力すると、ulがスクロールを開始しますが、liを含む最後のformが常に存在し、ユーザーに表示可能。

次のようなscrollToBottom関数を実装しようとしました。

scrollToBottom() {
    this.formLi.scrollIntoView({ behavior: 'smooth' });
}

しかし、それは単にulを画面の一番上にジャンプさせ、その中のフォームを含むliを唯一の目に見えるものとして表示します。

これを達成するためのより良い方法はありますか?私が見つけた回答は少し古く、ReactDOMを使用する傾向があります。ありがとう!

CSS:

.Prompt-box .options-holder {
    list-style: none;
    padding: 0;
    width: 95%;
    margin: 12px auto;
    max-height: 600px;
    overflow-y: auto;
}

HTML:

<ul className='options-holder'>
    {
        this.state.items.map((item, index) => (
            <li key={item.id} className={`option ${index === 0 ? 'first' : ''}`}>
                <div className='circle' onClick={this.removeItem} />

                <p className='item-text'>{ item.text }</p>
            </li>
        ))
    }

    <li key={0} className={`option form ${this.state.items.length === 0 ? 'only' : ''}`} ref={el => (this.formLi = el)}>
        <div className='circle form' />

        <form className='new-item-form' onSubmit={this.handleSubmit}>
            <input 
                autoFocus 
                className='new-item-input' 
                placeholder='Type something and press return...' 
                onChange={this.handleChange} 
                value={this.state.text}
                ref={(input) => (this.formInput = input)} />
        </form>
    </li> 
</ul>
28
Zack Shapiro

react-scroll libraryを使用します。ただし、ulのIDを明示的に設定する必要があります。

import { animateScroll } from "react-scroll";

scrollToBottom() {
    animateScroll.scrollToBottom({
      containerId: "options-holder"
    });
}

SetStateコールバックでscrollToBottomを呼び出すことができます。

例えば

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

または、setTimeoutを使用します。

または、react-scrollからElementを設定し、特定のliまでスクロールすることもできます。

7
emil

チャットのようなインターフェイスを構築しているようです。 <ul> そしてその div.circle-form以下のような別のdivで:

ul{
  border:1px solid red;
  height:13em;
  overflow-y:auto;
  position:relative;
}
ul li{
  border-bottom:1px solid #ddd;
  list-style:none;
  margin-left:0;
  padding:6px;
}

.wrapper{
  background:#eee;
  height:15em;
  position:relative;
}
.circle-form{
  background:#ddd;
  height:2em;
  padding:3px;
  position:absolute;
  bottom:0;
  left:0;
  right:0;
  z-index:2;
}
.circle-form input[type=text]{
  padding:8px;
  width:50%;
}
<div class="wrapper">
   <ul id="list">
       <li>item</li>
       <li>item</li>
       <li>item</li>
       <li>item</li>
       <li>item</li>
       <li>item</li>
       <li>item</li>
       <li>item</li>
    </ul>
    <div class="circle-form">
        <input type="text" name="type_here" placeholder="Enter something..." autofocus/>
    </div>
</div>

[〜#〜] edit [〜#〜]

そして、javascriptでリストの一番下までスクロールします

var list = document.getElementById("list");
list.scrollTop = list.offsetHeight;
4
William

ユーザーインポートから取得したコンポーネントの配列をレンダリングする際に、同様の問題が発生しました。最終的には、componentDidUpdate()関数を使用して私のものを機能させました。

componentDidUpdate() {
        // I was not using an li but may work to keep your div scrolled to the bottom as li's are getting pushed to the div
        const objDiv = document.getElementById('div');
        objDiv.scrollTop = objDiv.scrollHeight;
      }
2
Chris Paro

プロジェクトの1つで上にスクロールするために使用したスクリプトがあります。スムーズに、divの高さ(下にスクロール)をスクロールするためのリファクタリングを少し行いました。

scroll.js

function currentYPosition() {
  if (self.pageYOffset) return self.pageYOffset;
  if (document.documentElement && document.documentElement.scrollHeight) return document.documentElement.scrollHeight;
  if (document.body.scrollHeight) return document.body.scrollHeight;

  return 0;
}

function elmYPosition(eID) {
  let Elm = document.getElementById(eID);
  let y = Elm.offsetHeight;
  let node = Elm;
  while (node.offsetParent && node.offsetParent != document.body) {
    node = node.offsetParent;
    y += node.offsetHeight;
  }
  return y;
}

export default function smoothScroll(eID, string) {
  let startY = currentYPosition();
  let stopY = elmYPosition(eID);
  let distance = stopY > startY ? stopY - startY : startY - stopY;
  let speed = Math.round(distance / 10);
  let speedTimeout = 250;
  if (speed >= 100) speed = 100;
  if (string) speed = 1;
  let step = Math.round(distance / 25);
  let leapY = stopY > startY ? startY + step : startY - step;
  let timer = 0;
  if (stopY > startY) {
    for (let i = startY; i < stopY; i += step) {
      setTimeout('window.scrollTo(0, ' + leapY + ')', timer * speed);
      leapY += step;
      if (leapY > stopY) leapY = stopY;
      timer++;
    }
    return;
  }
  for (let i = startY; i > stopY; i -= step) {
    setTimeout('window.scrollTo(0, ' + (leapY) + ')', timer * speed);
    leapY -= step;
    if (leapY < stopY){
      leapY = stopY;
    } 
    timer++;
  }
}

これをコンポーネント内にインポートする必要があります。2つのパラメーターがあります(要素のID、この場合はrefを使用できます。2番目のパラメーターは、スクロールの速度を処理するために使用した文字列です。

import scroll from './your-path/scroll.js';
.
.
.
<ul className='options-holder'>
{
    this.state.items.map((item, index) => (
        <li key={item.id} className={`option ${index === 0 ? 'first' : ''}`} ref={el => (this.formLi = el)}>
            <div className='circle' onClick={this.removeItem} />

            <p className='item-text'>{ item.text }</p>
        </li>
    ))
}

<li key={0} className={`option form ${this.state.items.length === 0 ? 'only' : ''}`} ref={el => (this.formLi = el)}>
    <div className='circle form' />

    <form className='new-item-form' onSubmit={this.handleSubmit}>
        <input 
            autoFocus 
            className='new-item-input' 
            placeholder='Type something and press return...' 
            onChange={this.handleChange} 
            value={this.state.text}
            ref={(input) => (this.formInput = input)} />
    </form>
</li> 

このLIをレンダー内でどのようにマッピングするかをIdkで確認しますが、検証を行う必要があります。プロパティOverflowがある場合は、スクロールを実行する必要があります。

コンポーネントには最初の要素にジャンプするという合理的な答えがあります。最後ではなく、最初の要素の参照にヒットしています。

可能な回避策:

scroll(this.state.items[this.state.items.length - 1]);

更新1:元のscroll.jsの要点、 上部へスクロール

1
Yuri Ramos

単にposition:sticky s.t.最後のリストアイテムが常に表示されます。

li:last-child {
  position:sticky;
  bottom:0;
}

デモ

Caniuse

1
user943702