К счастью, есть. Свойство CSS называется overflow-anchor
. Если для свойства overflow-anchor
элемента установлено значение auto
, он включит привязку прокрутки ; что является попыткой браузера минимизировать проблему скачков контента (например, большое изображение, загружаемое выше вашей позиции прокрутки, заставляет ваш контент прокручиваться дальше вниз); и устанавливают указанный элемент в качестве потенциального якоря при настройке положения прокрутки.
При этом overflow-anchor
автоматически устанавливается на auto
, и вам не нужно его устанавливать , поэтому я не уверен, как настройка вручную помогла вам. С MDN :
Режим привязки прокрутки включен по умолчанию в любом браузере, который его поддерживает. Поэтому изменение значения этого свойства обычно требуется только в том случае, если у вас возникают проблемы с привязкой прокрутки в документе или части документа и вам необходимо отключить поведение.
Вот простая демонстрация. Сначала window
уже прокручивается на 200 пикселей. Каждые две секунды новый div
вставляется поверх контейнера. Обратите внимание, что видимая область window
не изменяется, даже если новый div
вставлен поверх контейнера.
const container = document.querySelector('#container')
window.scrollBy(0, 200)
let counter = 9
setInterval(() => {
let newDiv = document.createElement('div')
newDiv.classList.add('messages')
newDiv.innerText = counter++;
container.prepend(newDiv)
}, 2000)
* {
box-sizing: border-box;
font-family: Helvetica;
}
html,
body {
margin: 0;
}
#container {
width: 100%;
height: 100%;
padding: 0 20px;
scroll-behavior: smooth;
overflow-anchor: auto;
}
.messages {
width: 100%;
padding: 20px;
text-align: center;
background: #121212;
color: white;
border-radius: 5px;
margin: 20px 0px;
}
<div id="container">
<div class="messages">8</div>
<div class="messages">7</div>
<div class="messages">6</div>
<div class="messages">5</div>
<div class="messages">4</div>
<div class="messages">3</div>
<div class="messages">2</div>
<div class="messages">1</div>
</div>
Обновление
Настройка overflow-anchor
вручную помогла вам настроить положение прокрутки при увеличении высоты над областью просмотра , но Safari не поддерживает overflow-anchor
, вот простой альтернативный механизм (использующий requestAnimationFrame
) для настройки области просмотра так, чтобы она всегда оставалась в своей текущей позиции. Этот код ниже также регулирует положение прокрутки, когда вы находитесь в top контейнера, и появляются старые сообщения ( проблема, о которой вы упоминали ).
Обратите внимание, что я Я установил overflow-anchor
на none
, таким образом, отключение прокрутки с привязкой обычно включается автоматически, чтобы показать, что этот код работает (я запускаю его в Firefox). Кроме того, код настроен так, что он не прокручивается вниз, когда новые messages
добавляются к container
. Рассмотрите сценарий, когда пользователь читает последнее сообщение и новое сообщение отправляется; Вы можете прокрутить вниз в этом случае. Кроме того, я звоню getBoundingClientRect()
каждый кадр. При неправильном использовании это может привести к разбиванию макета (также прочитайте здесь для списка JS свойств / методов, которые могут вызвать это).
Один последний примечание: на самом деле я хотел использовать ResizeObserver
, но, поскольку Safari не поддерживает этот API, вместо этого я использовал rAQ
для наблюдения за изменениями высоты контейнера. Уже есть ResizeObserver
полифилы, которые я еще не опробовал, но может быть более эффективным, чем использование rAQ
для наблюдения за изменениями высоты. Вот страница , которая содержит множество ссылок на ResizeObserver
полифиллы.
const container = document.querySelector('#container')
let counter = 9
setInterval(() => {
let newDiv = document.createElement('div')
newDiv.classList.add('messages')
newDiv.innerText = counter++;
container.prepend(newDiv)
}, 2000)
window.scrollBy(0, 200)
// Mimicking scroll anchoring behaviour from browser
let previousLastChild = container.lastElementChild
let previousBoundingRect = container.getBoundingClientRect()
function scrollAdjustment() {
let boundingRect = container.getBoundingClientRect()
let isHeightIncreased = boundingRect.height !== previousBoundingRect.height
let isAppend = container.lastElementChild !== previousLastChild // Is height increase caused by new messages being appended?
if (isHeightIncreased && !isAppend) { // If new messages are appended, don't scroll down as people are reading the upper messages
let newScrollYPosition = window.scrollY + boundingRect.height - previousBoundingRect.height
previousBoundingRect = boundingRect
window.scrollTo(0, newScrollYPosition)
}
requestAnimationFrame(scrollAdjustment)
}
requestAnimationFrame(scrollAdjustment)
* {
box-sizing: border-box;
font-family: Helvetica;
}
html,
body {
margin: 0;
}
#container {
width: 100%;
height: 100%;
padding: 0 20px;
scroll-behavior: smooth;
overflow-anchor: none; /* Note I've set it to none */
}
.messages {
width: 100%;
padding: 20px;
text-align: center;
background: #121212;
color: white;
border-radius: 5px;
margin: 20px 0px;
}
<div id="container">
<div class="messages">8</div>
<div class="messages">7</div>
<div class="messages">6</div>
<div class="messages">5</div>
<div class="messages">4</div>
<div class="messages">3</div>
<div class="messages">2</div>
<div class="messages">1</div>
</div>