Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can it flexbox? Chat window with input at the bottom, chats scrolling up

Tags:

css

flexbox

Seems like it should be possible with flexbox, but I can't figure it out.

http://codepen.io/MichaelJCole/pen/NGBVGe

Goals:

  1. textarea (for typeing in messages) stays at the bottom the whole time.
  2. chats start at the bottom, then scroll up as needed.
  3. If you use the "Google Hangouts", like the message app in that.

Here's the markup:

 <div id="chatBar">
    <div id="chatList">
      <div class="chat mine">hello world</div>
      <div class="chat theirs">hello moon</div>
    </div>
    <input id="chatBarInput" class="form-control" type="textarea">
  </div>

And here's the CSS:

html, body { height: 100%; }
#chatBar {
  height: 100%;
  display: flex;
  flex-flow: column nowrap;
  justify-content: flex-end;
  overflow: none;
}

#chatList {
  flex: 0 1 auto;
  display: flex;
  flex-flow: column nowrap;
  justify-content: flex-end;
  overflow-y: scroll;
}

#chatBarInput {
  flex: 1 0 auto;
}

.chat {
  flex: none;
  align-self: flex-start;
  background-color: lightgreen;
}

.chat.mine {
  align-self: flex-end;
  background-color: pink;
}

I can't get #chatBar to "squeeze" #chatList without setting a height. Which is what I was trying to avoid by using flexbox :-/

Sorry, I'm a backend coder. Tried a bunch of stuff, then pared it down for the CodePen.

Seems like I should be able to tell the inner flexbox to scroll, while leaving the outer alone. Do I have to use position:absolute?

like image 775
Michael Cole Avatar asked Nov 04 '15 04:11

Michael Cole


People also ask

Can't scroll to top of flex item that is overflowing container?

To fix this problem use flexbox auto margins, instead of justify-content . With auto margins, an overflowing flex item can be vertically and horizontally centered without losing access to any part of it.

Can you use flexbox on text?

Flexbox allows us to vertically center our text much more easily and more accurately than the old-fashioned way.

Is flexbox responsive?

Flexbox is a relatively new front-end feature that makes building a website layout (and making it responsive!) much, much easier than it used to be. In days past, to build a website, you would have to use float grids or even tables to make your layout look like it should.

What are two main parts of flexbox layout?

The two axes of flexboxThe main axis is defined by the flex-direction property, and the cross axis runs perpendicular to it. Everything we do with flexbox refers back to these axes, so it is worth understanding how they work from the outset.


2 Answers

I can't get #chatBar to "squeeze" #chatList without setting a height. Which is what I was trying to avoid by using flexbox

You had the flex-basis set to auto for all elements. Without explicit height, the flex model will automatically try to accommodate everything inside the available space by shrinking or expanding the elements. This is why you are unable to get the #chatList to work as intended. The div itself as well as the individual chats all expand or shrink within the available space.

What you should do is to start simple:

#chatBar {
    height: 100%; overflow: hidden;
    display: flex; flex-flow: column;
}
#chatList {
    /* grow or shrink as required from flex-basis height of 20% */
    flex: 1 1 20%; 
    display: flex; flex-direction: column; 
    overflow: auto;
} 

/* do not grow or shrink with a flex-basis height of 80% */
#chatBarInput { flex: 0 0 80%; }

And you will be able to see it working. You could then take it further from here.

Your modified codepen: http://codepen.io/Abhitalks/pen/ZbjNvQ/


Goals:

  1. textarea (for typeing in messages) stays at the bottom the whole time.
  2. chats start at the bottom, then scroll up as needed.
  3. If you use the "Google Hangouts", like the message app in that.

The trick would be to use flex-direction: column-reverse and prepend the new messages to the container instead of appending those.

I took an old answer of mine and changed the layout to flex-model for a demo of this purpose. You can peruse the code to see how it's done.

Demo Fiddle: http://jsfiddle.net/abhitalks/khj4903t/

Demo Snippet:

var btn 	= document.getElementById('btn'), 
    inp 	= document.getElementById('inp'), 
    chats	= document.getElementById('chatWindow')
;
btn.addEventListener('click', postMsg);

inp.addEventListener('keyup', function(e) {
	if (e.keyCode == 13) { postMsg(); }
});

function postMsg() {
	var msg 	= inp.value,
        bubble 	= document.createElement('div'),
        p 		= document.createElement('p');
    if (msg.trim().length <= 0) { return; }
    bubble.classList.add('bubble');
    bubble.classList.add('right');
    p.textContent = msg;
    bubble.appendChild(p);
    inp.value = '';
    chats.insertBefore(bubble, chats.firstChild);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; overflow: hidden; }
.wrap { 
    margin: 8px; height: 90%; width: 50%; 
    display: flex; flex-direction: column;
}
.container {
    flex: 1 1 90%; display: flex; flex-direction: column; 
    background-color: #eee; border: 1px solid #ccc; overflow: auto;
}
.form { flex: 0 0 32px; display: flex; border: 1px solid #ddd; }
.form > input[type=text] { flex: 1 1 auto; border: 1px solid #eee; }
.form > input[type=button] { flex: 0 0 20%; border: 1px solid #eee; }
.bubble { flex: 1 1 auto; clear: both; } /* clear the floats here on parent */
.bubble p {
    border-radius: 5px;
    padding: 8px; margin: 8px 12px;
    max-width: 80%;  /* this will make it not exceed 80% and then wrap */
    position: relative; transition: background-color 0.5s; 
}
.left p { background-color: #ccc; float: left; } /* floated left */
.right p { background-color: #33c; color: #fff; float: right; } /* floated right */
/* classes below are only for arrows, not relevant */
.left p::before {
    content: ''; position: absolute;
    width: 0; height: 0; left: -8px; top: 8px;
    border-top: 4px solid transparent;
    border-right: 8px solid #ccc;
    border-bottom: 4px solid transparent;
}
.right p::after {
    content: ''; position: absolute;
    width: 0; height: 0; right: -8px; bottom: 8px;
    border-top: 4px solid transparent;
    border-left: 8px solid #33c;
    border-bottom: 4px solid transparent;
}
<div class="wrap">
    <div id="chatWindow" class="container">
        <div class="bubble left"><p>msg</p></div>
        <div class="bubble left"><p>long message</p></div>
        <div class="bubble right"><p>ultra long message which can wrap at eighty percent </p></div>
        <div class="bubble left"><p>lorem ipsum</p></div>
        <div class="bubble right"><p>very long message</p></div>    
        <div class="bubble right"><p>one more message</p></div>    
        <div class="bubble left"><p>lorem ipsum</p></div>
        <div class="bubble right"><p>another message</p></div>    
        <div class="bubble left"><p>lorem ipsum</p></div>
        <div class="bubble right"><p>yet another message</p></div>    
        <div class="bubble left"><p>lorem ipsum</p></div>
    </div>
    <div id="inputWindow" class="form">
        <input id="inp" type="text" />
        <input id="btn" type="button" value="Send" />
    </div>
</div>
like image 124
Abhitalks Avatar answered Oct 09 '22 07:10

Abhitalks


The vertical scrollbar on the browser exists because you've set a height: 100% to the body, and the user agent stylesheet applies a default margin to the body, typically 8px all around. So, 100% + 16px launches the vertical scroll.

Add this to your CSS: body { margin: 0; }

To apply the scrollbar to the inner flexbox (.chatlist), here are two adjustments:

#chatList {
  flex: 0 1 75px; /* specify a height */
  display: flex;
  flex-flow: column nowrap;
  /* justify-content: flex-end; REMOVE */ 
  overflow-y: scroll;
}

DEMO: http://jsfiddle.net/5p2vy31p/1/

like image 30
Michael Benjamin Avatar answered Oct 09 '22 07:10

Michael Benjamin