47 votes

Faites défiler vers le bas d'un div débordant dans React

J'ai un ul avec un max-height et overflow-y: auto.

Lorsque l'utilisateur saisit assez d' li - éléments, l' ul commence à défiler, mais je veux le dernier li avec l' form en à toujours être présent et visible pour l'utilisateur.

J'ai essayé de mettre en œuvre un scrollToBottom fonction qui ressemble à ceci:

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

Mais cela ne fait que l' ul sauter vers le haut de l'écran et afficher l' li avec la forme en elle que la seule chose visible.

Est-il un meilleur moyen pour y parvenir? Réponses que j'ai trouver a tendance à être un peu plus âgés, et l'utilisation ReactDOM. Merci!

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>

13voto

emil Points 2343

Utilisez la bibliothèque react-scroll . Cependant, vous devrez définir explicitement l'ID de votre ul .

 import { animateScroll } from "react-scroll";

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

Vous pouvez appeler scrollToBottom lors du rappel setState.

Par exemple

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

Ou en utilisant setTimeout.

Ou vous pouvez même définir Element partir de react-scroll et faire défiler jusqu'à un certain li .

11voto

Chris Paro Points 11

J'ai rencontré un problème similaire lors du rendu d'un tableau de composants provenant d'une importation utilisateur. J'ai fini par utiliser la fonction componentDidUpdate () pour faire fonctionner la mienne.

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

5voto

William Points 456

On dirait que vous êtes la construction d'une interface de chat-like. Vous pouvez essayer d'emballage l' <ul> et de la div.circle-form dans une autre div, comme ci-dessous:

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>

MODIFIER

Et pour faire défiler vers le bas de la liste avec javascript

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

1voto

user943702 Points 855

Utilisez simplement position:sticky st le dernier élément de la liste est toujours affiché.

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

Démo

Puis-je utiliser

1voto

Yuri Ramos Points 1237

J'ai un script que j'ai utilisé dans un de mes projets pour faire défiler de haut en Douceur, j'ai fait un peu de refactoriser le code pour faire défiler la hauteur de votre div (défilement en bas) j'espère que cela aide.

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

Vous devez importer ce à l'intérieur de votre composant, il y a 2 paramètres(l'ID de votre élément, dans ce cas, vous pouvez utiliser la ref. La seconde est une chaîne que j'ai utilisé pour traiter la vitesse de défilement.

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> 

Idk comment vous êtes cartographie de ce LI à l'intérieur de votre rendu, mais vous devriez faire une vérification et si il y a la propriété Overflow, vous devez exécuter le défilement.

Il y a une réponse raisonnable pour votre composant est en sautant sur le premier élément, vous frappez la ref pour le PREMIER élément, pas la dernière.

Solution Possible:

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

Mise à jour 1: Résumé de l'original scroll.js, défilement vers le haut

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X