Вставка элементов поверх чего-либо без изменения видимого прокручиваемого содержимого - PullRequest
0 голосов
/ 14 марта 2020

Я создаю интерфейс чата для веб-сайта. При первой загрузке отображаются самые последние сообщения, а самые новые находятся внизу, как это обычно происходит с большинством приложений чата.

По мере прокрутки пользователя все больше сообщений загружаются через наш API и вставляются выше. существующие сообщения.

Как и следовало ожидать, пузырьки чата имеют стиль <div> s внутри контейнера <div> с overflow-y: auto и установленной высотой.

В настоящее время я отмечаю верхнее сообщение <div>, загружая старые сообщения над ним, а затем перемещаю представление, чтобы попытаться вернуть пользователя почти туда, где он остановился, но это сложно. особенно когда пузыри чата содержат динамически загружаемые элементы (встроенные изображения и т. д. c). (Я не знаю высоту этих пузырьков до того, как браузер отобразит их.)

В идеальном мире я хотел бы найти способ вставить сообщения выше, не заставляя прокручиваемую позицию вообще двигаться. , чтобы пользователь не терял отслеживание того, где он находится в потоке сообщений.

Я никогда не слышал о таких вещах, но было бы здорово, если бы был способ заставить div растягиваться в направлении вверх, а не заставлять его sh сообщать о существующих сообщениях. вниз дальше.

Буду признателен за любой совет.

Вот JSFiddle, показывающий проблему основы c. Он имитирует мой интерфейс чата, загружая 30 сообщений чата, когда страница загружается, а затем ставит вас внизу списка сообщений. По мере прокрутки вверх загружаются еще 30, но вы увидите, что это раздражает UX, потому что вы мгновенно теряете свое место.

ОБНОВЛЕНИЕ: Благодаря ответу Ричарда, с техникой, основанной на requestAnimationFrame, для выравнивания области просмотра. (Для его использования я использовал scrollTop моего прокручиваемого div, а не window.scrollTo, как показано в его примере.)

1 Ответ

1 голос
/ 14 марта 2020

К счастью, есть. Свойство 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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...