Saya mencoba meniru aplikasi obrolan seluler lainnya di mana saat Anda memilih send-message
kotak teks dan membuka papan ketik virtual, pesan paling bawah masih terlihat. Sepertinya tidak ada cara untuk melakukan ini dengan CSS secara luar biasa, jadi JavaScript resize
(satu-satunya cara untuk mengetahui kapan keyboard dibuka dan ditutup ternyata) acara dan gulir manual untuk menyelamatkan.
Seseorang memberikan solusi ini dan saya menemukan solusi ini , yang sepertinya berfungsi baik.
Kecuali dalam satu kasus. Untuk beberapa alasan, jika Anda berada dalam MOBILE_KEYBOARD_HEIGHT
(250 piksel dalam kasus saya) piksel di bagian bawah div pesan, ketika Anda menutup keyboard ponsel, sesuatu yang aneh terjadi. Dengan solusi sebelumnya, itu bergulir ke bawah. Dan dengan solusi terakhir, ia akan menggulir ke atas MOBILE_KEYBOARD_HEIGHT
piksel dari bawah.
Jika Anda digulir di atas ketinggian ini, kedua solusi yang disediakan di atas berfungsi dengan sempurna. Hanya ketika Anda berada di dekat bagian bawah mereka memiliki masalah kecil ini.
Saya pikir mungkin itu hanya program saya yang menyebabkan ini dengan beberapa kode nyasar yang aneh, tapi tidak, saya bahkan mereproduksi biola dan memiliki masalah yang tepat ini. Maafkan saya karena membuat ini sangat sulit untuk di-debug, tetapi jika Anda membuka https://jsfiddle.net/t596hy8d/6/show (suffix show menyediakan mode layar penuh) pada ponsel Anda, Anda seharusnya dapat melihat perilaku yang sama.
Perilaku itu adalah, jika Anda menggulir ke atas cukup, membuka dan menutup keyboard mempertahankan posisi. Namun, jika Anda menutup keyboard dalam MOBILE_KEYBOARD_HEIGHT
piksel bagian bawah, Anda akan menemukan keyboard itu menggulir ke bagian bawah.
Apa yang menyebabkan ini?
Reproduksi kode di sini:
window.onload = function(e){
document.querySelector(".messages").scrollTop = 10000;
bottomScroller(document.querySelector(".messages"));
}
function bottomScroller(scroller) {
let scrollBottom = scroller.scrollHeight - scroller.scrollTop - scroller.clientHeight;
scroller.addEventListener('scroll', () => {
scrollBottom = scroller.scrollHeight - scroller.scrollTop - scroller.clientHeight;
});
window.addEventListener('resize', () => {
scroller.scrollTop = scroller.scrollHeight - scrollBottom - scroller.clientHeight;
scrollBottom = scroller.scrollHeight - scroller.scrollTop - scroller.clientHeight;
});
}
.container {
width: 400px;
height: 87vh;
border: 1px solid #333;
display: flex;
flex-direction: column;
}
.messages {
overflow-y: auto;
height: 100%;
}
.send-message {
width: 100%;
display: flex;
flex-direction: column;
}
<div class="container">
<div class="messages">
<div class="message">hello 1</div>
<div class="message">hello 2</div>
<div class="message">hello 3</div>
<div class="message">hello 4</div>
<div class="message">hello 5</div>
<div class="message">hello 6 </div>
<div class="message">hello 7</div>
<div class="message">hello 8</div>
<div class="message">hello 9</div>
<div class="message">hello 10</div>
<div class="message">hello 11</div>
<div class="message">hello 12</div>
<div class="message">hello 13</div>
<div class="message">hello 14</div>
<div class="message">hello 15</div>
<div class="message">hello 16</div>
<div class="message">hello 17</div>
<div class="message">hello 18</div>
<div class="message">hello 19</div>
<div class="message">hello 20</div>
<div class="message">hello 21</div>
<div class="message">hello 22</div>
<div class="message">hello 23</div>
<div class="message">hello 24</div>
<div class="message">hello 25</div>
<div class="message">hello 26</div>
<div class="message">hello 27</div>
<div class="message">hello 28</div>
<div class="message">hello 29</div>
<div class="message">hello 30</div>
<div class="message">hello 31</div>
<div class="message">hello 32</div>
<div class="message">hello 33</div>
<div class="message">hello 34</div>
<div class="message">hello 35</div>
<div class="message">hello 36</div>
<div class="message">hello 37</div>
<div class="message">hello 38</div>
<div class="message">hello 39</div>
</div>
<div class="send-message">
<input />
</div>
</div>
Jawaban:
Saya akhirnya menemukan solusi yang benar - benar berfungsi. Meskipun mungkin tidak ideal, itu sebenarnya berfungsi dalam semua kasus. Ini kodenya:
Beberapa pencerahan yang saya jalani:
Saat menutup keyboard virtual,
scroll
acara terjadi secara instan sebelumresize
acara. Ini sepertinya hanya terjadi ketika menutup keyboard, bukan membukanya. Ini adalah alasan Anda tidak dapat menggunakanscroll
acara untuk mengaturpxFromBottom
, karena jika Anda berada di dekat bagian bawah itu akan mengatur sendiri ke 0 discroll
acara tepat sebelumresize
acara, mengacaukan perhitungan.Alasan lain mengapa semua solusi mengalami kesulitan di dekat bagian bawah div pesan agak sulit dipahami. Sebagai contoh, dalam solusi ukuran saya, saya hanya menambah atau mengurangi 250 (tinggi keyboard ponsel)
scrollTop
ketika membuka atau menutup keyboard virtual. Ini berfungsi dengan baik kecuali di dekat bagian bawah. Mengapa? Karena katakanlah Anda 50 piksel dari bawah dan tutup keyboard. Ini akan mengurangi 250 dariscrollTop
(ketinggian keyboard), tetapi seharusnya hanya mengurangi 50! Jadi itu akan selalu mengatur ulang ke posisi tetap yang salah saat menutup keyboard di dekat bagian bawah.Saya juga percaya Anda tidak dapat menggunakan
onFocus
danonBlur
acara untuk solusi ini, karena itu hanya terjadi ketika awalnya memilih kotak teks untuk membuka keyboard. Anda dapat dengan sempurna membuka dan menutup keyboard ponsel tanpa mengaktifkan acara ini, dan karenanya, mereka tidak dapat digunakan di sini.Saya percaya poin di atas penting untuk mengembangkan solusi, karena mereka tidak jelas pada awalnya, tetapi mencegah solusi yang kuat dari pengembangan.
Saya tidak suka solusi ini (intervalnya sedikit tidak efisien dan rentan terhadap kondisi balapan), tetapi saya tidak dapat menemukan yang lebih baik yang selalu berfungsi.
sumber
Saya pikir apa yang Anda inginkan
overflow-anchor
Dukungan meningkat, tetapi tidak total, namun https://caniuse.com/#feat=css-overflow-anchor
Dari artikel CSS-Trik di atasnya:
Ini adalah versi yang sedikit dimodifikasi dari salah satu contohnya:
Buka ini di ponsel: https://cdpn.io/chasebank/debug/PowxdOR
Apa yang dilakukan pada dasarnya menonaktifkan penahan default elemen pesan baru
#scroller * { overflow-anchor: none }
Dan alih-alih menempelkan elemen kosong
#anchor { overflow-anchor: auto }
yang akan selalu datang setelah pesan-pesan baru, karena pesan-pesan baru dimasukkan sebelum itu.Harus ada gulir untuk melihat perubahan penahan, yang menurut saya umumnya baik UX. Namun demikian, posisi gulir saat ini harus dipertahankan ketika keyboard terbuka.
sumber
Solusi saya sama dengan solusi yang Anda ajukan dengan tambahan pemeriksaan bersyarat. Inilah uraian solusi saya:
scrollTop
dan terakhirclientHeight
dari.messages
untukoldScrollTop
danoldHeight
masing-masingoldScrollTop
danoldHeight
setiap kaliresize
terjadiwindow
dan perbaruioldScrollTop
setiap kaliscroll
terjadi.messages
window
menyusut (ketika keyboard virtual menunjukkan), ketinggian.messages
akan secara otomatis ditarik. Perilaku yang dimaksudkan adalah untuk membuat konten paling bawah dari.messages
masih terlihat bahkan ketika.messages
'memendek. Hal ini mengharuskan kita untuk secara manual mengatur posisi gulirscrollTop
dari.messages
.scrollTop
dari.messages
memastikan bahwa bagian paling bawah dari.messages
sebelum pencabutan tingginya terjadi adalah masih terlihatscrollTop
dari.messages
memastikan bahwa bagian paling bawah dari.messages
sisa-sisa bagian paling bawah dari.messages
setelah ekspansi tinggi (kecuali ekspansi tidak bisa terjadi ke atas, ini terjadi ketika Anda hampir di bagian atas.messages
)Apa yang menyebabkan masalah?
Pemikiran logis saya (mungkin cacat) adalah:
resize
terjadi,.messages
'perubahan tinggi, pembaruan.messages
scrollTop
terjadi di dalamresize
pengendali acara kami . Namun, setelah.messages
ekspansi tinggi, sebuahscroll
peristiwa anehnya terjadi sebelumresize
! Dan yang lebih aneh lagi,scroll
kejadian itu hanya terjadi ketika kita menyembunyikan keyboard ketika kita telah menggulir di atas nilai maksimumscrollTop
ketika.messages
tidak ditarik. Dalam kasus saya, ini berarti bahwa ketika saya gulir di bawah270.334px
(maksimumscrollTop
sebelum.messages
ditarik) dan menyembunyikan keyboard, itu anehscroll
sebelumresize
acara terjadi dan menggulir Anda.messages
ke persis270.334px
. Ini jelas mengacaukan solusi kami di atas.Untungnya, kita bisa mengatasi ini. Deduksi pribadi saya tentang mengapa ini
scroll
sebelumresize
peristiwa terjadi adalah karena.messages
tidak dapat mempertahankanscrollTop
posisinya di atas270.334px
ketika mengembang tinggi (ini sebabnya saya menyebutkan bahwa pemikiran logis awal saya cacat; hanya karena tidak ada cara untuk.messages
mempertahankanscrollTop
posisinya di atas maksimum nilai) . Oleh karena itu, ia segera menetapkanscrollTop
nilai maksimum yang dapat diberikannya (yaitu, tidak mengejutkan,270.334px
).Apa yang bisa kita lakukan?
Karena kita hanya memperbarui
oldHeight
pada pengubahan ukuran, kita dapat memeriksa apakah scroll paksa ini (atau lebih tepatnya,resize
) terjadi dan jika tidak, tidak memperbaruioldScrollTop
(karena kita sudah menangani itu diresize
!) Kita hanya perlu membandingkanoldHeight
dan ketinggian saat ini discroll
untuk melihat apakah pengguliran paksa ini terjadi. Ini berfungsi karena kondisioldHeight
tidak sama dengan ketinggian saatscroll
ini hanya akan benar ketikaresize
terjadi (yang kebetulan ketika pengguliran paksa terjadi).Berikut kode (dalam JSFiddle) di bawah:
Diuji pada Firefox dan Chrome untuk seluler dan berfungsi untuk kedua browser.
sumber