J'ai un ul
avec un max-height
et overflow-y: auto
.
Lorsque l'utilisateur entre suffisamment d'éléments li
, le ul
commence à défiler, mais je veux que le dernier li
avec le form
dedans soit toujours présent et visible par l'utilisateur.
J'ai essayé d'implémenter une fonction scrollToBottom
qui ressemble à ceci:
scrollToBottom() {
this.formLi.scrollIntoView({ behavior: 'smooth' });
}
Mais cela fait juste sauter le ul
en haut de l'écran et afficher le li
avec le formulaire comme seule chose visible.
Existe-t-il une meilleure façon d'accomplir cela? Les réponses que je trouve ont tendance à être un peu plus anciennes et à utiliser 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>
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 faites défiler jusqu'à un certain li
.
On dirait que vous construisez une interface de type chat. Vous pouvez essayer d'envelopper le <ul>
et le div.circle-form
dans une div séparée 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;
J'ai eu 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;
}
J'ai un script que j'ai utilisé dans l'un de mes projets pour faire défiler le haut en douceur, j'ai fait un petit refactor pour faire défiler la hauteur de votre div (défiler en bas) j'espère que ça 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 ceci dans votre composant, il y a 2 paramètres (l'ID de votre élément, dans ce cas, vous pouvez utiliser ref. Le second est une chaîne que j'ai utilisée 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 mappez ce LI à l'intérieur de votre rendu, mais vous devez faire une vérification et s'il y a la propriété Overflow, vous devez exécuter le défilement.
Il y a une réponse raisonnable pour que votre composant passe au premier élément, vous frappez la référence pour le PREMIER élément, pas le dernier.
Solution possible:
scroll(this.state.items[this.state.items.length - 1]);
Mise à jour 1: Gist of the original scroll.js, scrolling to the top