62

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.

j08691
  • 204,283
  • 31
  • 260
  • 272
Srdjan Dejanovic
  • 1,267
  • 2
  • 18
  • 31

6 Answers6

102

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
  • 21,046
  • 5
  • 50
  • 72
  • Just noticed that this is a copy of my answer. What is the difference? – lbartolic Aug 16 '18 at 10:50
  • 6
    @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
  • This worked great. Note: for Tailwind folks, it's `first:mt-auto`. – Jordan F Sep 26 '22 at 13:18
  • 1
    Interestingly the FF issue says it is `RESOLVED FIXED`, but I've just encountered this issue today in an up to date version... – Bence Szalai Nov 10 '22 at 13:10
  • 3
    `#container::before{content: ''; display: block; margin-top: auto; }` is better than `!important` – Ye Shiqing Nov 27 '22 at 10:26
  • 1
    This solution works. However, content will be scrolled to the top when the scrollbar shows. Still need to figure out how to get the content to start scrolled to the bottom, like it would with `flex-end`, hopefully without JS. – OXiGEN Dec 05 '22 at 01:01
55

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
  • 1,165
  • 2
  • 12
  • 24
  • 2
    Finally, the only answer that got this right, without saying it's a bug. You did not address the sender/receiver alignment, which can be solved with a simple property `align-self` on each class. – Fuzzical Logic May 30 '16 at 00:36
  • Yes, you are right. I focused on aligning main containers (header/content/footer). – lbartolic May 31 '16 at 12:56
  • 1
    The jsfiddle does not work on Safari 9.1.1. The scrollbar does not show up. – Eric Jul 19 '16 at 04:41
  • 6
    The jsfiddle also fails in Firefox 50.0.2 - The scrollbar shows up, but it's grayed out. – Aaa Dec 06 '16 at 04:32
  • 4
    This only seems to work on Chrome and Safari (11.0) but not in Firefox nor IE. – bgondy Oct 04 '17 at 16:02
  • adding 'min-height: 0;' on child will show the scrollbar in Firefox – Tripurari Shankar Jun 11 '19 at 10:02
  • 1
    The only problem with this approach is that the content is now in the inverse order, from bottom to top. Still a bug 7 years later! – rodgco Apr 06 '23 at 16:25
  • column-reverse didn't work for me since I need them in normal order, so margin-top answer really saved the day for me. – Gishas Jul 22 '23 at 14:07
19

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,486
  • 7
  • 33
  • 57
  • Sadly this does not work when the scrollbar appears, i.e. when the scrollbar appears I also want the messages to keep staying at the bottom – Bao Huynh Lam Jul 06 '23 at 15:21
7

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
  • 767
  • 5
  • 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
3

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,209
  • 6
  • 38
  • 252
-1

This solution worked for me:

display: flex;
flex-direction: row-reverse;
justify-content: flex-start;
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
  • This solution does work. The problem is now content logic has to use prepends instead of appends. That can get really confusing fast for apps like chat where this type of view is needed. – OXiGEN Dec 05 '22 at 00:56