html 滚动到React中溢出div的底部

4ioopgfo  于 2022-12-09  发布在  React
关注(0)|答案(9)|浏览(179)

我有一个ul,其中有一个max-height和一个overflow-y: auto
当用户输入足够多的li元素时,ul开始滚动,但我希望最后一个包含formli始终对用户显示和可见。
我尝试实现了一个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;
}

于飞:

<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>
sh7euo9m

sh7euo9m1#

我有一个填充页面高度的容器。这个容器有display flex和flex direction列。然后我有一个Messages组件,它是一个ul,每个消息有一个li,加上一个特殊的AlwaysScrollToBottom组件,作为最后一个子组件,在useEffect的帮助下,滚动到列表的底部。

const AlwaysScrollToBottom = () => {
  const elementRef = useRef();
  useEffect(() => elementRef.current.scrollIntoView());
  return <div ref={elementRef} />;
};

const Messages = ({ items }) => (
  <ul>
    {items.map(({id, text}) => <li key={id}>{text}</li>)}
    <AlwaysScrollToBottom />
  </ul>
)

const App = () => (
  <div className="container">
    <Messages items={[...]}>
    <MessageComposer />
  </div>
)

CSS是

.container {
  display: flex;
  height: 100vh;
  flex-direction: column;
}

ul {
  flex-grow: 1;
  overflow-y: auto;
}

MessageComposer,它包含用于发送消息的表单、输入字段等。

xfb7svmp

xfb7svmp2#

使用react-scroll库。但是您必须显式设置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

ssm49v7z

ssm49v7z3#

当渲染来自用户导入的组件数组时,我也遇到了类似的问题。

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;
      }
7y4bm7vi

7y4bm7vi4#

看起来你正在构建一个类似聊天的界面。你可以尝试将<ul>div.circle-form Package 在一个单独的div中,如下所示:
第一个

编辑

并使用javascript滚动到列表底部。

var list = document.getElementById("list");
list.scrollTop = list.offsetHeight;
2cmtqfgy

2cmtqfgy5#

我有一个脚本,我已经在我的一个项目中使用滚动顶部平滑,我做了一个小的重构滚动您的div的高度(滚动底部),我希望它有帮助。

滚动.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++;
  }
}

你应该把它导入到你的组件里面,有两个参数(你的元素的ID,在这个例子中,你可以使用ref。第二个是我用来处理滚动速度的字符串。

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>

我不知道你是如何Map这个LI在你的渲染,但你应该作出验证,如果有属性溢出,你应该运行滚动。
有一个合理的答案,你的组件是跳到第一个元素,你击中了第一个元素的参考,而不是最后一个。

可能的解决方法:

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

**更新1:**原始scroll.js,scrolling to the top的要点

ifmq2ha2

ifmq2ha26#

只需使用position:sticky s.t.,最后一个列表项将始终显示。

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

Demo
卡尼乌斯

tkqqtvp1

tkqqtvp17#

我用一个通用的实用函数解决了这个问题,这个函数可以在任何特定的组件之外工作。你需要在父元素(列表框,多行编辑,等等)和最后一个子元素(或者你想滚动到的任何地方)上定义一个引用。

export const scrollToRef = (parentRef, childRef) => {
    trace("scrollToRef:ref.current.offsetTop", childRef.current.offsetTop);
    parentRef.current.scrollTo(0, childRef.current.offsetTop)
}

我使用它作为一个功能组件,并调用它作为一个效果,每当我的父容器得到更新。

2o7dmzc5

2o7dmzc58#

answer here不太适合我,因为我使用的是CSS网格而不是Flexbox,所以添加的div创建了另一个网格行。
为了解决这个问题,我详细地创建了一个钩子,它接受ref,并将最后一个子对象获取到scrollIntoView()
第一个

6qftjkof

6qftjkof9#

您可以使用"react-scroll-to-bottom"模块来完成此操作。
添加新子项时自动滚动到底部。

import ScrollToBottom from 'react-scroll-to-bottom';

<ScrollToBottom className={ROOT_CSS}>
   <p>
       Nostrud nisi duis veniam ex esse laboris consectetur officia et. Velit 
       cillum est veniam culpa magna sit
  </p>
</ScrollToBottom>

链接:https://www.npmjs.com/package/react-scroll-to-bottom

相关问题