2017-08-16 16 views
26

Tôi có một ul với max-heightoverflow-y: auto.Cuộn xuống cuối một div bị tràn trong React

Khi người dùng nhập vào đủ li yếu tố, các ul bắt đầu di chuyển, nhưng tôi muốn cuối cùng li với form trong đó luôn luôn có mặt và có thể xem được cho người dùng.

tôi đã cố gắng thực hiện một chức năng scrollToBottom trông như thế này:

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

Nhưng điều đó chỉ làm cho ul nhảy tới phía trên cùng của màn hình và hiển thị các li mẫu tại nó như là chỉ có thể nhìn thấy Điều.

Có cách nào tốt hơn để thực hiện việc này không? Câu trả lời tôi tìm thấy có xu hướng lớn hơn một chút và sử dụng ReactDOM. Cảm ơn!

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> 
+0

Trong trường hợp này, có thể bạn di chuyển li cuối cùng ra khỏi ul và thêm một trình bao bọc khác –

+0

Tại sao tôi muốn làm điều đó thay vì chỉ cuộn ul? –

Trả lời

1

Tôi đã một kịch bản mà tôi đã sử dụng trong một trong những dự án của tôi để di chuyển đầu êm, tôi đã làm một chút cấu trúc lại để di chuyển chiều cao của div của bạn (cuộn dưới) Tôi hy vọng nó sẽ giúp.

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

Bạn nên nhập này bên trong thành phần của bạn, có 2 thông số (ID của nguyên tố của bạn, trong trường hợp này, bạn có thể sử dụng ref. Điều thứ hai là một chuỗi mà tôi đã sử dụng để điều trị tốc độ của cuộn.

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> 

Làm thế nào bạn đang lập bản đồ này LI bên trong render của bạn nhưng bạn nên thực hiện một xác minh và nếu có tài sản tràn, bạn nên chạy di chuyển.

Có một câu trả lời hợp lý cho thành phần của bạn là nhảy đến phần tử đầu tiên, bạn đang nhấn ref cho phần tử FIRST, không phải là phần tử đầu tiên.

thể workaround:

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

Cập nhật 1: Gist của scroll.js gốc, scrolling to the top

+0

Cảm ơn Yuri. Để rõ ràng, chỉ có 'li' với' form' bên trong nó có thẻ 'ref' trên đó. Không phải những cái khác. Cách giải quyết có thể không hoạt động vì 'ref' cũng không' this' có hàm 'scroll' –

2

Có vẻ như bạn đang xây dựng một giao diện trò chuyện như thế nào.Bạn có thể thử gói các <ul>div.circle-form trong một div riêng biệt như dưới đây:

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

Và để cuộn xuống cuối danh sách với javascript

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

Tôi không sử dụng jquery trong ứng dụng của mình và không có ý định –

+0

Đã chỉnh sửa từ jquery thành vani javascript – William

2

Sử dụng thư viện react-scroll. Tuy nhiên, bạn sẽ phải đặt ID rõ ràng là ul.

import { animateScroll } from "react-scroll"; 

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

Bạn có thể gọi scrollToBottom trên setState gọi lại.

Ví dụ

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

Hoặc bằng cách sử dụng setTimeout.

Hoặc thậm chí bạn có thể đặt Element từ react-scroll và cuộn đến một số nhất định li.

1

Chỉ cần sử dụng position:sticky s.t. mục danh sách cuối cùng luôn được hiển thị.

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

Demo

Caniuse

1

Tôi đã có một vấn đề tương tự khi nó đến để render một loạt các thành phần xuất thân từ một khẩu người dùng. Tôi đã kết thúc bằng cách sử dụng hàm componentDidUpdate() để làm cho tôi hoạt động.

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; 
     } 
Các vấn đề liên quan