Ich versuche, andere mobile Chat-Apps nachzuahmen. Wenn Sie das send-message
Textfeld auswählen und die virtuelle Tastatur öffnen, wird die unterste Meldung weiterhin angezeigt. Es scheint erstaunlicherweise keine Möglichkeit zu geben, dies mit CSS zu tun, also JavaScript- resize
Ereignisse (nur um herauszufinden, wann die Tastatur offenbar geöffnet und geschlossen wird) und manuelles Scrollen zur Rettung.
Jemand hat diese Lösung bereitgestellt , und ich habe diese Lösung herausgefunden , die beide zu funktionieren scheinen.
Außer in einem Fall. Aus irgendeinem Grund MOBILE_KEYBOARD_HEIGHT
passiert etwas Seltsames , wenn Sie sich innerhalb (250 Pixel in meinem Fall) Pixel am unteren Rand des Nachrichtendivs befinden, wenn Sie die mobile Tastatur schließen. Bei der ersteren Lösung wird nach unten gescrollt. Bei der letzteren Lösung werden stattdessen MOBILE_KEYBOARD_HEIGHT
Pixel von unten nach oben gescrollt.
Wenn Sie über diese Höhe gescrollt werden, funktionieren beide oben angegebenen Lösungen einwandfrei. Nur wenn Sie sich ganz unten befinden, haben sie dieses kleine Problem.
Ich dachte, vielleicht war es nur mein Programm, das dies mit einem seltsamen Streucode verursachte, aber nein, ich habe sogar eine Geige reproduziert und es hat genau dieses Problem. Ich entschuldige mich dafür, dass das Debuggen so schwierig ist. Wenn Sie jedoch auf Ihrem Telefon zu https://jsfiddle.net/t596hy8d/6/show (das Show-Suffix bietet einen Vollbildmodus) gehen, sollten Sie das sehen können gleiches Verhalten.
Wenn Sie genug nach oben scrollen, bleibt beim Öffnen und Schließen der Tastatur die Position erhalten. Wenn Sie jedoch in der Nähe der Tastatur innerhalb MOBILE_KEYBOARD_HEIGHT
Pixel des Bodens, werden Sie feststellen , dass es nach unten scrollt statt.
Was verursacht das?
Code-Reproduktion hier:
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>
Antworten:
Ich habe endlich eine Lösung gefunden, die tatsächlich funktioniert. Obwohl es möglicherweise nicht ideal ist, funktioniert es tatsächlich in allen Fällen. Hier ist der Code:
Einige Offenbarungen, die ich unterwegs hatte:
Beim Schließen der virtuellen Tastatur
scroll
tritt unmittelbar vor demresize
Ereignis ein Ereignis auf . Dies scheint nur beim Schließen der Tastatur zu geschehen, nicht beim Öffnen. Dies ist der Grund, warum Sie dasscroll
Ereignis nicht zum Festlegen verwenden könnenpxFromBottom
, da es sich imscroll
Ereignis unmittelbar vor demresize
Ereignis auf 0 setzt , wenn Sie sich in der Nähe des unteren Bereichs befinden, was die Berechnung durcheinander bringt .Ein weiterer Grund, warum alle Lösungen am Ende des Nachrichtendivs Schwierigkeiten hatten, ist etwas schwierig zu verstehen. Zum Beispiel addiere oder subtrahiere ich in meiner Größenänderungslösung nur 250 (Höhe der mobilen Tastatur),
scrollTop
wenn ich die virtuelle Tastatur öffne oder schließe. Dies funktioniert perfekt, außer in der Nähe des Bodens. Warum? Nehmen wir an, Sie sind 50 Pixel von unten entfernt und schließen die Tastatur. Es werden 250 vonscrollTop
(der Tastaturhöhe) subtrahiert, aber es sollten nur 50 subtrahiert werden! Daher wird es beim Schließen der Tastatur in der Nähe der Unterseite immer auf die falsche feste Position zurückgesetzt.Ich glaube auch, dass Sie
onFocus
undonBlur
Ereignisse für diese Lösung nicht verwenden können , da diese nur auftreten, wenn Sie zunächst das Textfeld zum Öffnen der Tastatur auswählen. Sie können die mobile Tastatur problemlos öffnen und schließen, ohne diese Ereignisse zu aktivieren. Daher können sie hier nicht verwendet werden.Ich glaube, die oben genannten Punkte sind wichtig für die Entwicklung einer Lösung, da sie zunächst nicht offensichtlich sind, aber die Entwicklung einer robusten Lösung verhindern.
Ich mag diese Lösung nicht (das Intervall ist etwas ineffizient und anfällig für Rennbedingungen), aber ich kann nichts Besseres finden, das immer funktioniert.
quelle
Ich denke was du willst ist
overflow-anchor
Der Support nimmt zu, ist aber noch nicht vollständig. Https://caniuse.com/#feat=css-overflow-anchor
Aus einem CSS-Tricks-Artikel dazu:
Hier ist eine leicht modifizierte Version eines ihrer Beispiele:
Öffnen Sie dies auf dem Handy: https://cdpn.io/chasebank/debug/PowxdOR
Grundsätzlich wird die Standardverankerung der neuen Nachrichtenelemente mit deaktiviert
#scroller * { overflow-anchor: none }
Und stattdessen ein leeres Element verankern
#anchor { overflow-anchor: auto }
, das immer nach diesen neuen Nachrichten kommt, da die neuen Nachrichten davor eingefügt werden .Es muss eine Schriftrolle geben, um eine Änderung der Verankerung zu bemerken, was meiner Meinung nach im Allgemeinen eine gute UX ist. In beiden Fällen sollte die aktuelle Bildlaufposition beim Öffnen der Tastatur beibehalten werden.
quelle
Meine Lösung ist die gleiche wie Ihre vorgeschlagene Lösung mit einer zusätzlichen bedingten Prüfung. Hier ist eine Beschreibung meiner Lösung:
scrollTop
und zuletztclientHeight
von.messages
zuoldScrollTop
undoldHeight
jeweilsoldScrollTop
undoldHeight
jedes Mal, wenn einresize
passiert,window
undoldScrollTop
jedes Mal, wenn einscroll
passiert.messages
window
es verkleinert wird (wenn die virtuelle Tastatur angezeigt wird), wird die Höhe.messages
automatisch zurückgezogen. Das beabsichtigte Verhalten besteht darin, den untersten Inhalt von auch dann.messages
noch sichtbar zu machen, wenn sich.messages
die Höhe zurückzieht. Dies erfordert, dass wir die BildlaufpositionscrollTop
von manuell anpassen.messages
.scrollTop
von.messages
angezeigt, um sicherzustellen, dass der unterste Teil.messages
vor dem Zurückziehen der Höhe weiterhin sichtbar istscrollTop
von.messages
um sicherzustellen , dass der unterste Teil der.messages
Überreste des unterste Teil des.messages
nach der Höhenausdehnung (es sei denn , Expansion kann nicht nach oben passieren, das passiert , wenn man fast an der Spitze ist.messages
)Was hat das Problem verursacht?
Mein (anfänglich möglicherweise fehlerhaftes) logisches Denken lautet:
resize
passiert,.messages
'Höhenänderungen, Aktualisierung.messages
scrollTop
erfolgt in unseremresize
Event-Handler. Bei.messages
einerscroll
Höhenerweiterung tritt jedoch seltsamerweise ein Ereignis vor einemresize
! Und noch merkwürdiger ist, dass dasscroll
Ereignis nur auftritt, wenn wir die Tastatur ausblenden, wenn wir über den Maximalwert gescrollt haben,scrollTop
wenn.messages
nicht zurückgezogen wird. In meinem Fall bedeutet dies, dass, wenn ich nach unten scrolle270.334px
(das MaximumscrollTop
vor dem.messages
Zurückziehen) und die Tastatur ausblende, das seltsam ist,scroll
bevor einresize
Ereignis eintritt und Sie.messages
genau zu scrollen270.334px
. Dies bringt unsere obige Lösung offensichtlich durcheinander.Glücklicherweise können wir das umgehen. Mein persönlicher Schluss, warum dies
scroll
vor demresize
Ereignis geschieht, ist, dass ich.messages
seinescrollTop
Position von oben nicht beibehalten kann,270.334px
wenn es sich in der Höhe ausdehnt (aus diesem Grund habe ich erwähnt, dass mein anfängliches logisches Denken fehlerhaft ist; einfach, weil es keine Möglichkeit gibt.messages
, seinescrollTop
Position über seinem Maximum zu halten Wert) . Daher setzt es sofortscrollTop
den Maximalwert, den es geben kann (was nicht überraschend ist270.334px
).Was können wir tun?
Da wir nur beim Ändern der
oldHeight
Größe aktualisieren , können wir überprüfen, ob dieser erzwungene Bildlauf (oder korrekterresize
) erfolgt, und wenn dies der Fall ist, nicht aktualisierenoldScrollTop
(da wir dies bereits erledigt habenresize
!). Wir müssen lediglich vergleichenoldHeight
und die aktuelle Höhe aktivierenscroll
um zu sehen, ob dieses erzwungene Scrollen passiert. Dies funktioniert, weil die Bedingung,oldHeight
nicht gleich der aktuellen Höhe zuscroll
sein, nur dann erfüllt ist, wennresize
dies geschieht (was zufällig der Fall ist, wenn dieses erzwungene Scrollen auftritt).Hier ist der Code (in JSFiddle) unten:
Getestet auf Firefox und Chrome für Handys und funktioniert für beide Browser.
quelle