Я пытаюсь имитировать другие мобильные приложения чата, где при выборе send-message
текстового поля и открытии виртуальной клавиатуры самое нижнее сообщение все еще отображается. Кажется, не существует способа сделать это с помощью CSS, так что JavaScriptresize
(единственный способ выяснить, когда клавиатура открывается и закрывается, очевидно), события и ручная прокрутка для спасения.
Кто-то предоставил это решение, и я узнал это решение , которое, похоже, работает.
За исключением одного случая. По какой-то причине, если вы находитесь в пределахMOBILE_KEYBOARD_HEIGHT
(250 пикселей в моем случае) пикселей от нижней части div сообщений, когда вы закрываете мобильную клавиатуру, происходит нечто странное. С прежним решением, оно прокручивается до дна. И с последним решением, он вместо этого прокручивается вверхMOBILE_KEYBOARD_HEIGHT
пиксели снизу.
Если вы прокручиваетесь выше этой высоты, оба решения, представленные выше, работают безупречно. Только когда вы находитесь на дне, у них есть эта незначительная проблема.
Я подумал, может быть, это была просто моя программа, вызывающая это с каким-то странным заблудшим кодом, но нет, я даже воспроизвел скрипку, и она имеет именно эту проблему. Приношу свои извинения за то, что это так сложно отлаживать, но если вы перейдете на https://jsfiddle.net/t596hy8d/6/show (суффикс show предоставляет полноэкранный режим) на вашем телефоне, вы сможете увидеть такое же поведение
Такое поведение, если вы прокручиваете достаточно вверх, открытие и закрытие клавиатуры сохраняет положение. Однако, если вы закроете клавиатуру в пределах MOBILE_KEYBOARD_HEIGHT
пикселей снизу, вы обнаружите, что она прокручивается вниз.
Что вызывает это?
Воспроизведение кода здесь:
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>
Ответы:
Я наконец нашел решение, которое действительно работает. Хотя это не может быть идеальным, на самом деле это работает во всех случаях. Вот код:
Некоторые прозрения, которые у меня были по пути:
При закрытии виртуальной клавиатуры
scroll
событие происходит сразу передresize
событием. Кажется, это происходит только при закрытии клавиатуры, а не при ее открытии. Это причина, по которой вы не можете использоватьscroll
событие для установкиpxFromBottom
, потому что, если вы находитесь рядом с дном, оно будет установлено на 0 вscroll
событии непосредственно передresize
событием, что испортит расчет.Еще одна причина, по которой у всех решений возникли сложности в нижней части div сообщений, немного сложна для понимания. Например, в моем решении по изменению размера я просто добавляю или убираю 250 (высота мобильной клавиатуры)
scrollTop
при открытии или закрытии виртуальной клавиатуры. Это работает отлично, за исключением нижней части. Почему? Потому что, скажем, вы 50 пикселей снизу и закройте клавиатуру. Это вычтет 250 изscrollTop
(высоты клавиатуры), но это должно только вычесть 50! Таким образом, он всегда будет сброшен в неправильное фиксированное положение при закрытии клавиатуры в нижней части.Я также считаю, что вы не можете использовать
onFocus
иonBlur
события для этого решения, потому что они происходят только при первоначальном выборе текстового поля для открытия клавиатуры. Вы прекрасно можете открывать и закрывать мобильную клавиатуру, не активируя эти события, и поэтому они не могут использоваться здесь.Я полагаю, что вышеперечисленные пункты важны для разработки решения, так как поначалу они неочевидны, но мешают выработке надежного решения.
Мне не нравится это решение (интервал немного неэффективен и подвержен гонкам), но я не могу найти ничего лучше, что всегда работает.
источник
Я думаю, что вы хотите
overflow-anchor
Поддержка увеличивается, но не полностью, но https://caniuse.com/#feat=css-overflow-anchor
Из статьи CSS-Tricks:
Вот немного измененная версия одного из их примеров:
Откройте это на мобильном телефоне: https://cdpn.io/chasebank/debug/PowxdOR
То, что он делает, в основном отключает любую привязку по умолчанию для новых элементов сообщения, с
#scroller * { overflow-anchor: none }
И вместо этого прикрепление пустого элемента
#anchor { overflow-anchor: auto }
, который всегда будет после этих новых сообщений, так как новые сообщения вставляются перед ним.Должна быть прокрутка, чтобы заметить изменение в привязке, что, как мне кажется, в целом является хорошим UX. Но в любом случае текущая позиция прокрутки должна сохраняться при открытии клавиатуры.
источник
Мое решение совпадает с предложенным вами решением с добавлением условной проверки. Вот описание моего решения:
scrollTop
и последнююclientHeight
из.messages
доoldScrollTop
иoldHeight
соответственноoldScrollTop
иoldHeight
каждый раз, когдаresize
происходит,window
и обновлятьoldScrollTop
каждый раз, когдаscroll
происходит.messages
window
сокращается (когда отображается виртуальная клавиатура), высота.messages
автоматически убирается. Предполагаемое поведение состоит в том, чтобы сделать самое нижнее содержимое.messages
все еще видимым, даже когда.messages
высота убирается. Для этого необходимо вручную отрегулировать положение прокруткиscrollTop
в.messages
.scrollTop
ее,.messages
чтобы убедиться, что самая нижняя часть.messages
до ее отвода по высоте все еще виднаscrollTop
ее,.messages
чтобы убедиться, что самая нижняя часть.messages
остается самой нижней частью.messages
после увеличения высоты (если расширение не может происходить вверх; это происходит, когда вы почти наверху.messages
)Что вызвало проблему?
Мое (первоначальное, возможно, ошибочное) логическое мышление:
resize
случается,.messages
высота меняется, обновление.messages
scrollTop
происходит внутри нашегоresize
обработчика событий. Тем не менее, после.messages
увеличения высоты,scroll
событие, как ни странно, происходит передresize
! И что еще более любопытно,scroll
событие происходит только тогда, когда мы скрываем клавиатуру, когда мы прокручиваем выше максимальногоscrollTop
значения, когда.messages
не убирается. В моем случае это означает, что когда я прокручиваю ниже270.334px
(максимумscrollTop
до.messages
убирается) и скрываю клавиатуру, это странноscroll
до того, какresize
происходит событие, и прокручивает.messages
точно270.334px
. Это очевидно портит наше решение выше.К счастью, мы можем обойти это. Мой личный вывод о том, почему это происходит
scroll
доresize
события, заключается в том, что я.messages
не могу поддерживать своюscrollTop
позицию выше,270.334px
когда она расширяется по высоте (вот почему я упомянул, что мое первоначальное логическое мышление имеет недостатки; просто потому, что нет способа.messages
сохранить своюscrollTop
позицию выше своего максимума. значение) . Следовательно, он немедленно устанавливаетscrollTop
максимальное значение, которое он может дать (что неудивительно270.334px
).Что мы можем сделать?
Поскольку мы обновляем только
oldHeight
при изменении размера, мы можем проверить, происходит ли эта принудительная прокрутка (или, точнее,resize
), и если это происходит, не обновлятьoldScrollTop
(потому что мы уже обработали этоresize
!). Нам просто нужно сравнитьoldHeight
и текущую высоту наscroll
чтобы увидеть, происходит ли эта принудительная прокрутка. Это работает, потому что условиеoldHeight
не быть равным текущей высотеscroll
будет выполнено только тогда, когда этоresize
происходит (что совпадает, когда происходит принудительная прокрутка).Вот код (в JSFiddle) ниже:
Протестировано на Firefox и Chrome для мобильных устройств и работает для обоих браузеров.
источник