javascript – Stick date to the top until a new dates appears during scroll like in WhatsApp-ThrowExceptions

Exception or error:

I’m trying to do the same with the date like WhatsApp does on mobile and web when you start scrolling. Heres a question about this topic but only in Android but I want to do this in HTML, CSS and JS:

How to display a date like Whatsapp on scrolling ListView

So this is my actual layout I have:

#messages {
  border: 1px solid;
  max-height: 200px;
  overflow-y: scroll;
}

#messages > div { 
  margin-bottom: 15px;
  padding-left: 3%;
  padding-right: 3%;
}

.message.right {
  text-align: right;
}

.divider {
 text-align: center;
}

.divider span {
  padding: 6px 12px;
  text-align: center;
  line-height: 1;
  background: gray;
  color: #fff;
  border-radius: 6px;
}
<div id="messages">
  <div class="message left">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="divider">
    <span>22.02.2020</span>
  </div>
  <div class="message right">Testnachricht</div>
  <div class="message right">Testnachricht</div>
  <div class="message right">Testnachricht</div>
  <div class="message right">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="divider">
    <span>Heute</span>
  </div>
  <div class="message right">Testnachricht</div>
  <div class="message right">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="message right">Testnachricht</div>
  <div class="message right">Testnachricht</div>
  <div class="message right">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="message right">Testnachricht</div>
  <div class="message right">Testnachricht</div>
  <div class="message right">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="message right">Testnachricht</div>
</div>

Is there any possible way to detect when a date (22.02.2020 or Heute) scrolls out of the view with a bit of space from the top? If yes, how can I stick this at the top of my scrolling border box until the next date comes in?

UPDATE

Here you can see what I mean:

enter image description here

How to solve:

Here’s a JS solution to give you an idea of how you might do it with JS:

const topLabel = document.getElementById('date-label')
const messageBox = document.getElementById('messages')
messageBox.addEventListener('scroll', () => {
  const dateLabels = document.querySelectorAll('.divider')
  let currentLabel = null
  dateLabels.forEach((dateLabel) => {
    if(messageBox.scrollTop >= dateLabel.offsetTop)
    {
      currentLabel = dateLabel
    }
  })
  if(currentLabel) {
    topLabel.style.opacity = '1'
    topLabel.innerText = currentLabel.innerText
  } else {
    topLabel.style.opacity = '0'
  }
})
#messages {
  border: 1px solid;
  max-height: 200px;
  overflow-y: scroll;
  position: relative;
}

#messages > div { 
  margin-bottom: 15px;
  padding-left: 3%;
  padding-right: 3%;
}

.message.right {
  text-align: right;
}

.divider {
  text-align: center;
}

.divider span {
  display: inline-block;
  padding: 6px 12px;
  text-align: center;
  line-height: 1;
  background: gray;
  color: #fff;
  border-radius: 6px;
}

.sticky {
  position: sticky;
  top: 10px;
}
<div id="messages">
  <div class="divider sticky">
    <span id="date-label" style="opacity: 0;">22.02.2020</span>
  </div>
  <div class="message left">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="divider">
    <span>22.02.2020</span>
  </div>
  <div class="message right">Testnachricht</div>
  <div class="message right">Testnachricht</div>
  <div class="message right">Testnachricht</div>
  <div class="message right">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="divider">
    <span>Heute</span>
  </div>
  <div class="message right">Testnachricht</div>
  <div class="message right">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="message right">Testnachricht</div>
  <div class="message right">Testnachricht</div>
  <div class="message right">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="message right">Testnachricht</div>
  <div class="message right">Testnachricht</div>
  <div class="message right">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="message left">Testnachricht</div>
  <div class="message right">Testnachricht</div>
</div>

###

Use CSS’s position: sticky; You’ll need to enclose each dated group of messages in its own div. Below I have enclosed the dated groups in a div with class="date-area", just to identify them. I have only applied styles to .divider. You can see I added position: sticky; and top: 10px to stick the date ten pixels from the top of the viewport as you scroll:

Edit:
I’ve added .spacer divs at the bottom of the date groups, then offset the date labels toward the top by the same height as the spacers. This way the date labels overlap completely to give the feeling of ‘replacing’ the date. I can’t see any benefit to cloning the date label as WhatsApp does. The only way I can think of to fully mimic that behavior is by using JS, and I don’t consider it to be good practice to use JS for purely presentation purposes unless there is some tangible benefit. Where’s the benefit in completely duplicating WhatsApp’s design?

#messages {
  border: 1px solid;
  max-height: 200px;
  overflow-y: scroll;
}

#messages > div { 
  margin-bottom: 15px;
  padding-left: 3%;
  padding-right: 3%;
}

.message.right {
  text-align: right;
}

.divider {
  text-align: center;
  position: sticky;
  top: 10px;
}

.divider span {
  padding: 6px 12px;
  text-align: center;
  line-height: 1;
  background: gray;
  color: #fff;
  border-radius: 6px;
}

.date-area {
  margin-top: -35px;
}

.spacer {
  height: 35px;
}
<div id="messages">
  <div class="date-area">
    <div class="message left">Testnachricht</div>
    <div class="message left">Testnachricht</div>
    <div class="message left">Testnachricht</div>
    <div class="spacer">&nbsp;</div>
  </div>
  <div class="date-area">
    <div class="divider">
      <span>22.02.2020</span>
    </div>
    <div class="message right">Testnachricht</div>
    <div class="message right">Testnachricht</div>
    <div class="message right">Testnachricht</div>
    <div class="message right">Testnachricht</div>
    <div class="message left">Testnachricht</div>
    <div class="spacer">&nbsp;</div>
  </div>
  <div class="date-area">
    <div class="divider">
      <span>Heute</span>
    </div>
    <div class="message right">Testnachricht</div>
    <div class="message right">Testnachricht</div>
    <div class="message left">Testnachricht</div>
    <div class="message left">Testnachricht</div>
    <div class="message left">Testnachricht</div>
    <div class="message right">Testnachricht</div>
    <div class="message right">Testnachricht</div>
    <div class="message right">Testnachricht</div>
    <div class="message left">Testnachricht</div>
    <div class="message left">Testnachricht</div>
    <div class="message left">Testnachricht</div>
    <div class="message right">Testnachricht</div>
    <div class="message right">Testnachricht</div>
    <div class="message right">Testnachricht</div>
    <div class="message left">Testnachricht</div>
    <div class="message left">Testnachricht</div>
    <div class="message left">Testnachricht</div>
    <div class="message right">Testnachricht</div>
    <div class="spacer">&nbsp;</div>
  </div>
</div>

Leave a Reply

Your email address will not be published. Required fields are marked *