50

I have chat and I need to scroll all content to bottom. I want to use justify-content: flex-end and to have vertical scrollbar.

.session-textchat {
  height: 320px;
  background: #fff;
  display: -webkit-flex;
  display: flex;
  -webkit-align-items: flex-end;
  align-items: flex-end;
  -webkit-justify-content: space-between;
  justify-content: space-between;
  -webkit-flex-direction: column;
  flex-direction: column;
}
.session-textchat .past-messages {
  width: 100%;
  max-width: 980px;
  margin: 0 auto;
  height: 83.92%;
  overflow-y: auto;
  padding: 30px 0 0;
  display: -webkit-flex;
  display: flex;
  -webkit-align-items: flex-end;
  align-items: flex-end;
  -webkit-justify-content: flex-end;
  justify-content: flex-end;
  -webkit-flex-direction: column;
  flex-direction: column;
}
.session-textchat .past-messages .receiver,
.session-textchat .past-messages .sender {
  width: 100%;
  min-height: 47px;
  margin: 0 0 20px;
  display: -webkit-flex;
  display: flex;
  -webkit-flex-direction: row;
  flex-direction: row;
}
.session-textchat .past-messages .receiver .message,
.session-textchat .past-messages .sender .message {
  position: relative;
  padding: 17px;
  -moz-border-radius: 4px;
  -webkit-border-radius: 4px;
  border-radius: 4px;
}
.session-textchat .past-messages .receiver {
  text-align: left;
  -webkit-justify-content: flex-start;
  justify-content: flex-start;
}
.session-textchat .past-messages .receiver .message {
  background: #f4f4f4;
  color: #535353;
}
.session-textchat .past-messages .sender {
  text-align: right;
  -webkit-justify-content: flex-end;
  justify-content: flex-end;
}
.session-textchat .past-messages .sender .message {
  background: url('../img/rgbapng/0050ff26.png');
  background: rgba(0, 80, 255, 0.15);
  color: #0050ff;
}
<div class="session-textchat">
  <div class="past-messages">
    <div class="receiver">
      <span class="message">
            Good afternoon David. Welcome to your appointment! How are you today?
          </span>
    </div>
    <div class="sender">
      <span class="message">
            Hello doctor. I feel terrible to be honest.
          </span>
    </div>
    <div class="receiver">
      <span class="message">
            I can see from your notes that you've been having some ear ache - can you tell me a bit more about your symptoms?
          </span>
    </div>
    <div class="sender">
      <span class="message">
            Hello doctor. I feel terrible to be honest.
          </span>
    </div>
    <div class="receiver">
      <span class="message">
            I can see from your notes that you've been having some ear ache - can you tell me a bit more about your symptoms?
          </span>
    </div>
  </div>
</div>

Example is here.

Is it possible? Or please give me better solution.

Thanks in advance!
Srdjan

xpy
  • 5,177
  • 3
  • 31
  • 46
Srdjan Dejanovic
  • 1,147
  • 2
  • 17
  • 31

6 Answers6

84

I just had to face this issue myself and, after concluding it is a bug, I came up with a workaround.

In summary, don't use justify-content: flex-end but rather put a margin-top: auto on the first child. Unlike flex-end this doesn't break the scrollbar functionality, and it bottom-aligns the contents when they're not overflowing the container.

Example based on @SrdjanDejanovic's fiddle is at https://jsfiddle.net/peter9477/4t5r0t5b/

In case the example isn't available, here's the relevant CSS:

#container {
    overflow-y: auto;
    display: flex;
    flex-flow: column nowrap;
    /* justify-content: flex-end; DO NOT USE: breaks scrolling */
}
#container > :first-child {
    margin-top: auto !important;
    /* use !important to prevent breakage from child margin settings */
}

An alternative workaround that I believe I've also used is to add an extra container for the scrollbar. Use the flex-end on the inner container and have the outer container handle the scrolling. I generally dislike workarounds that require adding dummy elements though, so I prefer my CSS-only solution above.

Peter Hansen
  • 20,311
  • 3
  • 46
  • 71
  • Just noticed that this is a copy of my answer. What is the difference? – lbartolic Aug 16 '18 at 10:50
  • 4
    @lbartolic A copy? Not really... In fact there seems little in common between them. I certainly did not read yours before posting mine (that's not how I roll), and mine includes some links to background that yours does not (gathered after extensive research, I might add), and the margin-top part that's probably crucial to my answer isn't in yours, so they may have some small parts the same but they're clearly not. – Peter Hansen Nov 12 '18 at 19:48
  • I think 'maring-top: auto' is a good approach but works till we reach the height at which there is no space left at top, so margin auto doesn't help, e.g. scroll starts appearing. I had to apply manual scroll with scrollTop = scrollHeight to achieve the always scrolled bottom position. – Abhay Dec 27 '19 at 13:45
  • This solution even works well with the infamous [FireFox bug](https://bugzilla.mozilla.org/show_bug.cgi?id=1042151) and [flexbugs](https://github.com/philipwalton/flexbugs/issues/108). So, instead of `flex-direction`, using `flex-flow` and `margin-top` for some mysterious reason works. – iomario Feb 27 '20 at 00:16
  • Isn't `!important`, like, an anti-pattern? Can't the problem be solved without resorting to it? Did not read too thouroughly, but the jsfiddle from this answer does not seem to work anymore, the scroll starts from top in Chromium 94. – Klesun Nov 07 '21 at 07:23
42

Probably you've already solved this, but I faced this problem too and found a solution by trial and error, so I'm going to share it.

Having parent container's display set to flex display: flex and child's items align to flex-end align-items: flex-end will prevent overflow-y: auto to work.

Instead, you can leave you can use next CSS properties for your parent container (in your case session-textchat):

display: flex;
flex-direction: column-reverse; /* 'column' for start, 'column-reverse' for end */
overflow-y: scroll; /* or overflow-y: auto ... */

This will make your child div appear on the bottom of parent container (it will act like flex-end) and enable vertical scroll if content height is bigger than parent container.

I made a little jsfiddle for you if this sounds confusing: https://jsfiddle.net/lbartolic/9od4nruy/3/

In jsfiddle you can see header part, content part and footer. Container has fixed height and each part takes required height to fill the container. Content part _b__content will be scrollable if its content is taller than _b__content's height.

I hope this will help someone. Cheers.

lbartolic
  • 983
  • 1
  • 10
  • 23
14

Also There is also another Solution

Remove the justify-content and add flex: 1 1 auto; property to the first element(create an empty div)

Old

HTML

<div class="content-reversed">
  <div>Item 1</div>
  <div>Item 2</div>
  <div>Item 3</div>
</div>

CSS

.content-reversed {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

New

HTML

<div class="content-reversed">
  <div class="fix"></div> //add this dummy div
  <div>Item 1</div>
  <div>Item 2</div>
  <div>Item 3</div>
</div>

CSS

.content-reversed {
  display: flex;
  flex-direction: column;
}
.content-reversed .fix {
   flex: 1 1 auto;
 }
Sibiraj
  • 4,138
  • 6
  • 29
  • 55
4

It seems to be a common bug among the browsers.

You should distribute your style onto 2 containers: the outer will be scrolled, and the inner will be a flex container. Also, you need some js to keep your message list scrolled to bottom while adding new messages.

Here is an example of code:

markup:

<div id='outer'>
    <div id='inner-scroll'>
        <div id='inner-flex'>
            <div class='flex-item'>Item 1</div>
            <div class='flex-item'>Item 2</div>
            ...
        </div>
</div>

style:

#inner-scroll {
    height: 100%;
    overflow: auto;
}

#inner-flex {
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    min-height: 100%;
}

.flex-item { /*nothing*/ }

JS:

function messagePushCallback()
{
    var scrollable=document.getElementById('inner-scroll');
    scrollable.scrollTo(0, scrollable.scrollHeight-scrollable.clientHeight);
}

// for an example
chat.onMessagePush(messagePushCallback);

window.addEventListener('load', messagePushCallback);

In JS, scrollable.scrollHeight shows the whole height of the element, including the space beyond its visible part, while scrollable.clientHeight is for the height of the visible part.

Nikita Ivanov
  • 617
  • 4
  • 17
  • 2
    This solution works in both Firefox 57 and Chrome 63. The current most upvoted answer (using `column-reverse` instead of `flex-end`) doesn't seem to work in Firefox. – newprogrammer Jan 21 '18 at 21:56
2

You have to turn .session-textchat into a flex column then margin-top: auto on .past-messages to send it to the bottom. Then play with overflow-y: scroll and some jQuery:

function updateScroll() {
  $("#chat").animate({ scrollTop: $('#chat').prop("scrollHeight")}, 1000);
}
updateScroll();
$("#send_button").on('click', updateScroll);
.session-textchat {
  display: flex;
  flex-direction: column;
  height: 300px;
  margin-bottom: 30px;
  background: #fff;
  overflow-y: scroll;
}
.session-textchat .past-messages {
  margin-top: auto;
  width: 100%;
  max-width: 980px;
}
.session-textchat .past-messages .receiver,
.session-textchat .past-messages .sender {
  width: 100%;
  min-height: 47px;
  margin: 0 0 20px 0;
}
.session-textchat .past-messages .receiver .message,
.session-textchat .past-messages .sender .message {
  position: relative;
  padding: 15px;
  -moz-border-radius: 4px;
  -webkit-border-radius: 4px;
  border-radius: 4px;
}
.session-textchat .past-messages .receiver {
  text-align: left;
}
.session-textchat .past-messages .receiver .message {
  background: #f4f4f4;
  color: #535353;
}
.session-textchat .past-messages .sender {
  text-align: right;
}
.session-textchat .past-messages .sender .message {
  background: url("../img/rgbapng/0050ff26.png");
  background: rgba(0, 80, 255, 0.15);
  color: #0050ff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" rel="stylesheet" />

<div class="container">
  <div id="chat" class="session-textchat">
    <div class="past-messages">
      <div class="receiver">
        <span class="message">
        Good afternoon David. Welcome to your appointment! How are you today?
      </span>
      </div>
      <div class="sender">
        <span class="message">
        Hello doctor. I feel terrible to be honest.
      </span>
      </div>
      <div class="receiver">
        <span class="message">
        I can see from your notes that you've been having some ear ache - can you tell me a bit more about your symptoms?
      </span>
      </div>
      <div class="sender">
        <span class="message">
        Hello doctor. I feel terrible to be honest.
      </span>
      </div>
      <div class="receiver">
        <span class="message">
        I can see from your notes that you've been having some ear ache - can you tell me a bit more about your symptoms?
      </span>
      </div>
    </div>
  </div>
  <div class="form-group">
    <textarea class="form-control" rows="5" id="msg"></textarea>
  </div>
  <div class="form-group text-center">
    <button href="#" id="send_button" class="btn btn-success">Send message</button>
  </div>
</div>

Look at this full-screen jsFiddle.

Razvan Zamfir
  • 4,237
  • 4
  • 24
  • 201
-1

This solution worked for me:

display: flex;
flex-direction: row-reverse;
justify-content: flex-start;
Eric Aya
  • 69,000
  • 34
  • 174
  • 243