Overflow-y: auto without changing the content padding when the scrollbar appears - css

I am trying to create a content container that doesn't change the content position when overflow causes a scrollbar to appear. I can do this easily when my containers are fixed widths but I am struggling to think of a way where I can use percentages to scale to the browser width. I am using an extra container with a negative right margin so the scroll bar appears in the padding of the parent container.
How can I make the content div stay the same width as the header div? I either need to be able to create a variable with the width of the header and use it for the width of content or have some sort of css that targets a container that has overflow-y enabled.
const chars = 'abcdefghijklmnopqrstuzwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
const contentContainer = document.getElementsByClassName('content')[0];
function addContent(num) {
for (let i = 0; i < num; i++) {
contentContainer.innerHTML += chars[Math.floor(chars.length * Math.random())] + ' ';
}
}
addContent(150);
document.getElementById('add').addEventListener('click', function() {
addContent(1500);
});
.container {
height: 100px;
border: 1px solid black;
padding: 20px;
}
.header {
background-color: #eee;
padding: 10px;
}
.scrollbar {
margin-right: -20px;
height: 100%;
overflow-y: auto;
}
.content {
padding-right: 20px;
}
<div class="container">
<div class="header"></div>
<div class="scrollbar">
<div class="content"></div>
</div>
</div>
<button id="add">Add scrollbar</button>

Related

Scale font to size when text exceeds container?

I want font size to be 54px if the text fits inside the container, otherwise it should be 36px.
I was considering whether I can achieve this with a pure CSS solution, using the scale function to collapse to either of the two. If the container can be assumed to be full with, I guess I could use vw as a base for a calculation?
But I am very much stuck on this. Could anyone give me a hint, as to how I can achieve this or something close to it.
If you would like to put some text inside a container and have it size itself to fill that container, then CSS Tricks has an article on Fitting Text to a Container that will cover your options.
You could also use Viewport Sized Typography which take advantage of viewport units such as:
1vw = 1% of viewport width
1vh = 1% of viewport height
1vmin = 1vw or 1vh, whichever is smaller
1vmax = 1vw or 1vh, whichever is larger
One unit of any v* is 1% of the viewport axis. Where the “Viewport” == browser window size == window object. If the viewport is 50cm wide, 1vw == 0.5cm. Using viewport units alone such as font-size: 4vw; can make the text appear too big or too small when varying the window width and bring accessibility issues (as the user preferences are not taken into account).
Lastly, you could use clamp() to achieve Simplified Fluid Typography. Clamp takes three values, a min, max, and a flexible unit in the middle that it will use in case the value is between the min and max.
If you want the font size to be a minimum of 36px and maximum 54px, you could use clamp() like this and vary the "flexible unit" to your liking. Here is an example of fluid typography for an <h1> element inside a container.
body {
font-size: 1rem;
margin: 0 auto;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.container {
border: .2rem solid #f06;
padding: .5rem;
height: 100%;
width: auto;
}
.container h1 {
font-size: 36px; /* fallback */
font-size: clamp(36px, 10vw, 54px);
}
<body>
<div class="container">
<h1>Heading text</h1>
</div>
</body>
Browser support for clamp() is pretty good, but you’d probably want to put a font-size declaration before it to set an acceptable fallback value.
In conclusion, if you needed to set an explicit width and height for said container, you might want to use media queries along with viewport units, calc(), or clamp() depending on the size of the content box in which the text resides.
i think it's nearly impossible to calculate it without js.
below is an code example how i would do it in jquery or you could use the following jquery plugin, but i never tested this plugin before: FitText.js
for (let container of $('.text-container')){
container = $(container);
let textInner = $(container).find('.text-inner');
console.log(container.width());
console.log(textInner.width());
if (container.width()<textInner.width()){
textInner.addClass('fs-36');
textInner.removeClass('fs-54');
} else {
textInner.addClass('fs-54');
textInner.removeClass('fs-36');
}
}
.text-container,
.text-container-before{
position: relative;
width: 250px;
height: 60px;
background: #333;
color: #090;
overflow: visible;
margin: 20px 0;
}
.text-inner,
.text-before{
position: absolute;
top: 0;
left: 0;
height: 100%;
background: #0399;
word-break: keep-all;
white-space: nowrap;
}
.fs-54{
font-size: 54px;
}
.fs-36{
font-size: 36px;
}
/* just added because console window is hiding running code snipped */
.spacer{
height: 80px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1>before</h1>
<div class="text-container-before">
<div class="text-before fs-54">a bit longer text</div>
</div>
<div class="text-container-before">
<div class="text-before fs-54">shorter text</div>
</div>
<h1>after</h1>
<div class="text-container">
<div class="text-inner fs-54">a bit longer text</div>
</div>
<div class="text-container">
<div class="text-inner fs-54">shorter text</div>
</div>
<div class="spacer"></div>
This question awsers was not exactly what I was looking for, so here is a solution to scale down the text if it would exceed the size of the parent container, by making it smaller until it fits the container.
for(const element of document.getElementsByClassName("shrink"))
{
var size = parseInt(getComputedStyle(element).getPropertyValue('font-size'));
const parent_width = parseInt(getComputedStyle(element.parentElement).getPropertyValue('width'))
while(element.offsetWidth > parent_width)
{
element.style.fontSize = size + "px"
size -= 1
}
}
.container{
border:1px dashed #ccc;
display:flex;
align-items:center;
justify-content:center;
width:200px;
height:50px;
margin:10px;
}
.shrink{
white-space: nowrap;
font-size:80px;
}
<div class="container"><span class="shrink">long text to shrink</span></div>
<div class="container"><span class="shrink">small text</span></div>
try word-break: break-all;, I think this will solve your problem
Not, Sure about CSS solution. But, My JS solution can resolve your problem.
Refer below code. Class class-54 and class-36 will change based on content:
// creating node
function createNode(element) {
return document.createElement(element);
}
// creating append
function append(parent, el) {
return parent.appendChild(el);
}
var getPageTitle = document.getElementById("pageTitle"),
textLengthWidth = 0;
const getPageTitleText = getPageTitle.textContent;
getPageTitle.innerHTML = "";
for (let index = 0; index < getPageTitleText.length; index++) {
const span = createNode("span");
span.innerHTML = getPageTitleText.charAt(index);
append(getPageTitle, span);
if (index == getPageTitleText.length - 1) {
calcFontSize();
}
}
function calcFontSize() {
var listOfSpan = document.querySelectorAll("#pageTitle span");
listOfSpan.forEach(element => {
textLengthWidth += element.offsetWidth;
});
console.log("total DIV width: " + getPageTitle.offsetWidth);
console.log("total Content width: " + textLengthWidth);
getPageTitle.offsetWidth < textLengthWidth ? getPageTitle.classList.add("class-36") : getPageTitle.classList.add("class-54");
}
* {
padding: 0;
margin: 0;
font-size: 14px;
}
.title {
display: block;
width: 100vw;
}
h1, h1 span {
font-size: 54px;
}
.class-54 span {
font-size: 54px;
}
.class-36 span {
font-size: 36px;
}
<div class="title">
<h1 id="pageTitle">Hello World! Hello World!</h1>
</div>
Try this
.thingy {
font-size:54px;
overflow-wrap: break-word;
}
<h1 class="thingy">Hel000000000000000000ooo</h1>

Smaller horizontal scrollbar than div width? + No figcaption?

I'm trying to do image "carousel" with horizontal scroll. Pure HTML + CSS without JS.
This is my HTML:
<div class="slideshow">
<figure class="slideshow__fig">
<img src="img/hilti-png.png" alt="" class="slideshow__fig-img">
<figcaption>Fig.1 - Trulli, Puglia, Italy.</figcaption>
</figure>
<figure class="slideshow__fig">
<img src="img/hilti-png.png" alt="" class="slideshow__fig-img">
<figcaption>Fig.1 - Trulli, Puglia, Italy.</figcaption>
</figure>
<figure class="slideshow__fig">
<img src="img/hilti-png.png" alt="p" class="slideshow__fig-img">
<figcaption>Fig.1 - Trulli, Puglia, Italy.</figcaption>
</figure>
</div>
This is my css:
.slideshow {
display: flex;
flex-wrap: nowrap;
overflow-y: hidden;
overflow-x: scroll;
//width: 80vw;
//margin: auto;
&::-webkit-scrollbar {
width: 350px;
height: 10px;
}
/* Track */
&::-webkit-scrollbar-track {
background: #f1f1f1;
}
/* Handle */
&::-webkit-scrollbar-thumb {
background: #888;
border-radius: 500px;
}
/* Handle on hover */
&::-webkit-scrollbar-thumb:hover {
background: #555;
}
&__fig {
flex: 0 0 auto;
height: 900px;
&-img{
height: 100%;
//width: 100%;
display: block;
}
}
}
1 - The problem is when I set the width of the .slideshow to 80vw, then naturally the scrollbar is shorter, but my images are cropped and not going full display width. When I try to adjust the width of the scrollbar with ::-webkit-scrollbar {width: 350px or 50vw or ...} exactly nothing happens.
I would like to have a scrollbar which is not full width of the div which I'm scrolling, but somehow can't figure it out.
2 - The other problem is I would like to have a figcaption at the bottom left side of the image. But somehow it doesn't show when the horizontal scroll is there. Any suggestions?
Here is the example how I would like to have it:
example image
edit: Now I finally managed to do it by adding:
&::-webkit-scrollbar-button:end:increment {
width: 50%;
display: block;
background: transparent;
}
But now the problem is that the scrollbar is not in middle, but on the left side. Margin:auto doesn't help. No idea how else to do it.
Also making img size 90% revealed the caption which is not that bad solution.
Now the only question is how to put the scroll bar in the centre.
Here is something close to the image you provided as an example. Sorry, but I really don't know how this can be achieved respecting the Pure HTML + CSS without JS criteria. I think it isn't possible at all.
So here, it uses jQuery and jQuery-ui draggable.
It uses a draggable div contained within its parent. Ondrag, it calculates the "scrolled" percentage to apply it to the scrollable width of the image slider.
For mobiles... I added the "touch punch" patch for jQuery-ui. More details about it here. I also placed the "initialisation code" in a function, so it can run on load AND on resize.
$(document).ready(function(){
function initDisplay(){
let slide_scrollWidth = $("#slide")[0].scrollWidth;
let customScrollbar_width = $("#sliderScroll_outer")[0].scrollWidth;
let percent = slide_scrollWidth/customScrollbar_width
$("#sliderScroll").css({"width":percent+"%", "left":0})
$("#slide")[0].scrollTo(0,0)
}
// On page load
initDisplay()
// Useful for mobile orientation change
window.onresize = initDisplay
$("#sliderScroll").draggable({
containment: "#sliderScroll_outer",
scroll: false,
drag: function(e){
let parentOffset = $(e.target).parent().offset().left
let offset = $(e.target).offset().left
let scrollableWidth = $(e.target).parent().width() - $(e.target).width()
let sliderPercent = (offset-parentOffset)/scrollableWidth
//console.log(sliderPercent)
let imageSliderWidth = $("#slide")[0].scrollWidth - $("#slide").width()
//console.log(imageSliderWidth)
$("#slide")[0].scrollTo(sliderPercent*imageSliderWidth,0)
}
});
});
#container{
margin: 1em;
}
#slide{
display: flex;
overflow: hidden;
}
#slide img{
margin: 0 0.5em;
}
#sliderScroll_outer{
width: 40vw;
background: lightgrey;
margin: 1em;
}
#sliderScroll{
width: 0vw;
height: 10px;
background: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui-touch-punch/0.2.2/jquery.ui.touch-punch.min.js"></script>
<div id="container">
<div id="slide">
<img src="https://via.placeholder.com/800x600.png">
<img src="https://via.placeholder.com/400x600.png">
<img src="https://via.placeholder.com/1000x600.png">
</div>
<div id="sliderScroll_outer">
<div id="sliderScroll"></div>
</div>
</div>
Run in full page mode or CodePen

Chatbox component - Flex item with scrollable content [duplicate]

Here is an example chat app ->
The idea here is to have the .messages-container take up as much of the screen as it can. Within .messages-container, .scroll holds the list of messages, and in case there are more messages then the size of the screen, scrolls.
Now, consider this case:
The user scrolls to the bottom of the conversation
The .text-input, dynamically gets bigger
Now, instead of the user staying scrolled to the bottom of the conversation, the text-input increases, and they no longer see the bottom.
One way to fix it, if we are using react, calculate the height of text-input, and if anything changes, let .messages-container know
componentDidUpdate() {
window.setTimeout(_ => {
const newHeight = this.calcHeight();
if (newHeight !== this._oldHeight) {
this.props.onResize();
}
this._oldHeight = newHeight;
});
}
But, this causes visible performance issues, and it's sad to be passing messages around like this.
Is there a better way? Could I use css in such a way, to express that when .text-input-increases, I want to essentially shift up all of .messages-container
2:nd revision of this answer
Your friend here is flex-direction: column-reverse; which does all you ask while align the messages at the bottom of the message container, just like for example Skype and many other chat apps do.
.chat-window{
display:flex;
flex-direction:column;
height:100%;
}
.chat-messages{
flex: 1;
height:100%;
overflow: auto;
display: flex;
flex-direction: column-reverse;
}
.chat-input { border-top: 1px solid #999; padding: 20px 5px }
.chat-input-text { width: 60%; min-height: 40px; max-width: 60%; }
The downside with flex-direction: column-reverse; is a bug in IE/Edge/Firefox, where the scrollbar doesn't show, which your can read more about here: Flexbox column-reverse and overflow in Firefox/IE
The upside is you have ~ 90% browser support on mobile/tablets and ~ 65% for desktop, and counting as the bug gets fixed, ...and there is a workaround.
// scroll to bottom
function updateScroll(el){
el.scrollTop = el.scrollHeight;
}
// only shift-up if at bottom
function scrollAtBottom(el){
return (el.scrollTop + 5 >= (el.scrollHeight - el.offsetHeight));
}
In the below code snippet I've added the 2 functions from above, to make IE/Edge/Firefox behave in the same way flex-direction: column-reverse; does.
function addContent () {
var msgdiv = document.getElementById('messages');
var msgtxt = document.getElementById('inputs');
var atbottom = scrollAtBottom(msgdiv);
if (msgtxt.value.length > 0) {
msgdiv.innerHTML += msgtxt.value + '<br/>';
msgtxt.value = "";
} else {
msgdiv.innerHTML += 'Long long content ' + (tempCounter++) + '!<br/>';
}
/* if at bottom and is IE/Edge/Firefox */
if (atbottom && (!isWebkit || isEdge)) {
updateScroll(msgdiv);
}
}
function resizeInput () {
var msgdiv = document.getElementById('messages');
var msgtxt = document.getElementById('inputs');
var atbottom = scrollAtBottom(msgdiv);
if (msgtxt.style.height == '120px') {
msgtxt.style.height = 'auto';
} else {
msgtxt.style.height = '120px';
}
/* if at bottom and is IE/Edge/Firefox */
if (atbottom && (!isWebkit || isEdge)) {
updateScroll(msgdiv);
}
}
/* fix for IE/Edge/Firefox */
var isWebkit = ('WebkitAppearance' in document.documentElement.style);
var isEdge = ('-ms-accelerator' in document.documentElement.style);
var tempCounter = 6;
function updateScroll(el){
el.scrollTop = el.scrollHeight;
}
function scrollAtBottom(el){
return (el.scrollTop + 5 >= (el.scrollHeight - el.offsetHeight));
}
html, body { height:100%; margin:0; padding:0; }
.chat-window{
display:flex;
flex-direction:column;
height:100%;
}
.chat-messages{
flex: 1;
height:100%;
overflow: auto;
display: flex;
flex-direction: column-reverse;
}
.chat-input { border-top: 1px solid #999; padding: 20px 5px }
.chat-input-text { width: 60%; min-height: 40px; max-width: 60%; }
/* temp. buttons for demo */
button { width: 12%; height: 44px; margin-left: 5%; vertical-align: top; }
/* begin - fix for hidden scrollbar in IE/Edge/Firefox */
.chat-messages-text{ overflow: auto; }
#media screen and (-webkit-min-device-pixel-ratio:0) {
.chat-messages-text{ overflow: visible; }
/* reset Edge as it identifies itself as webkit */
#supports (-ms-accelerator:true) { .chat-messages-text{ overflow: auto; } }
}
/* hide resize FF */
#-moz-document url-prefix() { .chat-input-text { resize: none } }
/* end - fix for hidden scrollbar in IE/Edge/Firefox */
<div class="chat-window">
<div class="chat-messages">
<div class="chat-messages-text" id="messages">
Long long content 1!<br/>
Long long content 2!<br/>
Long long content 3!<br/>
Long long content 4!<br/>
Long long content 5!<br/>
</div>
</div>
<div class="chat-input">
<textarea class="chat-input-text" placeholder="Type your message here..." id="inputs"></textarea>
<button onclick="addContent();">Add msg</button>
<button onclick="resizeInput();">Resize input</button>
</div>
</div>
Side note 1: The detection method is not fully tested, but it should work on newer browsers.
Side note 2: Attach a resize event handler for the chat-input might be more efficient then calling the updateScroll function.
Note: Credits to HaZardouS for reusing his html structure
You just need one CSS rule set:
.messages-container, .scroll {transform: scale(1,-1);}
That's it, you're done!
How it works: First, it vertically flips the container element so that the top becomes the bottom (giving us the desired scroll orientation), then it flips the content element so that the messages won't be upside down.
This approach works in all modern browsers. It does have a strange side effect, though: when you use a mouse wheel in the message box, the scroll direction is reversed. This can be fixed with a few lines of JavaScript, as shown below.
Here's a demo and a fiddle to play with:
//Reverse wheel direction
document.querySelector('.messages-container').addEventListener('wheel', function(e) {
if(e.deltaY) {
e.preventDefault();
e.currentTarget.scrollTop -= e.deltaY;
}
});
//The rest of the JS just handles the test buttons and is not part of the solution
send = function() {
var inp = document.querySelector('.text-input');
document.querySelector('.scroll').insertAdjacentHTML('beforeend', '<p>' + inp.value);
inp.value = '';
inp.focus();
}
resize = function() {
var inp = document.querySelector('.text-input');
inp.style.height = inp.style.height === '50%' ? null : '50%';
}
html,body {height: 100%;margin: 0;}
.conversation {
display: flex;
flex-direction: column;
height: 100%;
}
.messages-container {
flex-shrink: 10;
height: 100%;
overflow: auto;
}
.messages-container, .scroll {transform: scale(1,-1);}
.text-input {resize: vertical;}
<div class="conversation">
<div class="messages-container">
<div class="scroll">
<p>Message 1<p>Message 2<p>Message 3<p>Message 4<p>Message 5
<p>Message 6<p>Message 7<p>Message 8<p>Message 9<p>Message 10<p>Message 11<p>Message 12<p>Message 13<p>Message 14<p>Message 15<p>Message 16<p>Message 17<p>Message 18<p>Message 19<p>Message 20
</div>
</div>
<textarea class="text-input" autofocus>Your message</textarea>
<div>
<button id="send" onclick="send();">Send input</button>
<button id="resize" onclick="resize();">Resize input box</button>
</div>
</div>
Edit: thanks to #SomeoneSpecial for suggesting a simplification to the scroll code!
Please try the following fiddle - https://jsfiddle.net/Hazardous/bypxg25c/. Although the fiddle is currently using jQuery to grow/resize the text area, the crux is in the flex related styles used for the messages-container and input-container classes -
.messages-container{
order:1;
flex:0.9 1 auto;
overflow-y:auto;
display:flex;
flex-direction:row;
flex-wrap:nowrap;
justify-content:flex-start;
align-items:stretch;
align-content:stretch;
}
.input-container{
order:2;
flex:0.1 0 auto;
}
The flex-shrink value is set to 1 for .messages-container and 0 for .input-container. This ensures that messages-container shrinks when there is a reallocation of size.
I've moved text-input within messages, absolute positioned it to the bottom of the container and given messages enough bottom padding to space accordingly.
Run some code to add a class to conversation, which changes the height of text-input and bottom padding of messages using a nice CSS transition animation.
The JavaScript runs a "scrollTo" function at the same time as the CSS transition is running to keep the scroll at the bottom.
When the scroll comes off the bottom again, we remove the class from conversation
Hope this helps.
https://jsfiddle.net/cnvzLfso/5/
var doScollCheck = true;
var objConv = document.querySelector('.conversation');
var objMessages = document.querySelector('.messages');
var objInput = document.querySelector('.text-input');
function scrollTo(element, to, duration) {
if (duration <= 0) {
doScollCheck = true;
return;
}
var difference = to - element.scrollTop;
var perTick = difference / duration * 10;
setTimeout(function() {
element.scrollTop = element.scrollTop + perTick;
if (element.scrollTop === to) {
doScollCheck = true;
return;
}
scrollTo(element, to, duration - 10);
}, 10);
}
function resizeInput(atBottom) {
var className = 'bigger',
hasClass;
if (objConv.classList) {
hasClass = objConv.classList.contains(className);
} else {
hasClass = new RegExp('(^| )' + className + '( |$)', 'gi').test(objConv.className);
}
if (atBottom) {
if (!hasClass) {
doScollCheck = false;
if (objConv.classList) {
objConv.classList.add(className);
} else {
objConv.className += ' ' + className;
}
scrollTo(objMessages, (objMessages.scrollHeight - objMessages.offsetHeight) + 50, 500);
}
} else {
if (hasClass) {
if (objConv.classList) {
objConv.classList.remove(className);
} else {
objConv.className = objConv.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
}
}
}
}
objMessages.addEventListener('scroll', function() {
if (doScollCheck) {
var isBottom = ((this.scrollHeight - this.offsetHeight) === this.scrollTop);
resizeInput(isBottom);
}
});
html,
body {
height: 100%;
width: 100%;
background: white;
}
body {
margin: 0;
padding: 0;
}
.conversation {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
position: relative;
}
.messages {
overflow-y: scroll;
padding: 10px 10px 60px 10px;
-webkit-transition: padding .5s;
-moz-transition: padding .5s;
transition: padding .5s;
}
.text-input {
padding: 10px;
-webkit-transition: height .5s;
-moz-transition: height .5s;
transition: height .5s;
position: absolute;
bottom: 0;
height: 50px;
background: white;
}
.conversation.bigger .messages {
padding-bottom: 110px;
}
.conversation.bigger .text-input {
height: 100px;
}
.text-input input {
height: 100%;
}
<div class="conversation">
<div class="messages">
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is the last message
</p>
<div class="text-input">
<input type="text" />
</div>
</div>
</div>
You write;
Now, consider this case:
The user scrolls to the bottom of the conversation
The .text-input, dynamically gets bigger
Wouldn't the method that dynamically sets the .text-input be the logical place to fire this.props.onResize().
To whom it may concern,
The answers above did not suffice my question.
The solution I found was to make my innerWidth and innerHeight variable constant - as the innerWidth of the browser changes on scroll to adapt for the scrollbar.
var innerWidth = window.innerWidth
var innerHeight = window.innerHeight
OR FOR REACT
this.setState({width: window.innerWidth, height: window.innerHeight})
In other words, to ignore it, you must make everything constant as if it were never scrolling. Do remember to update these on Resize / Orientation Change !
IMHO current answer is not a correct one:
1/ flex-direction: column-reverse; reverses the order of messages - I didn't want that.
2/ javascript there is also a bit hacky and obsolete
If you want to make it like a PRO use spacer-box which has properties:
flex-grow: 1;
flex-basis: 0;
and is located above messages. It pushes them down to the chat input.
When user is typing new messages and input height is growing the scrollbar moves up, but when the message is sent (input is cleared) scrollbar is back at bottom.
Check my snippet:
body {
background: #ccc;
}
.chat {
display: flex;
flex-direction: column;
width: 300px;
max-height: 300px;
max-width: 90%;
background: #fff;
}
.spacer-box {
flex-basis: 0;
flex-grow: 1;
}
.messages {
display: flex;
flex-direction: column;
overflow-y: auto;
flex-grow: 1;
padding: 24px 24px 4px;
}
.footer {
padding: 4px 24px 24px;
}
#chat-input {
width: 100%;
max-height: 100px;
overflow-y: auto;
border: 1px solid pink;
outline: none;
user-select: text;
white-space: pre-wrap;
overflow-wrap: break-word;
}
<div class="chat">
<div class="messages">
<div class="spacer-box"></div>
<div class="message">1</div>
<div class="message">2</div>
<div class="message">3</div>
<div class="message">4</div>
<div class="message">5</div>
<div class="message">6</div>
<div class="message">7</div>
<div class="message">8</div>
<div class="message">9</div>
<div class="message">10</div>
<div class="message">11</div>
<div class="message">12</div>
<div class="message">13</div>
<div class="message">14</div>
<div class="message">15</div>
<div class="message">16</div>
<div class="message">17</div>
<div class="message">18</div>
</div>
<div class="footer">
<div contenteditable role="textbox" id="chat-input"></div>
</div>
<div>
Hope I could help :)
Cheers

Make image maximum available width / height but without losing aspect ratio?

How can I resize an image so that it is as large as possible within its container without losing its aspect ratio?
This could be achieved with the picture element but I need to support IE9.
Note that in my example both the images are smaller than the container, but in reality some will be larger and have to be scaled down.
I cannot use background images and I would prefere not to use javascript if possible.
Images that are larger than there container can easily be scaled down with this:
img {
height: auto;
width: auto;
max-height: 100%;
max-width: 100%;
}
Therefore is there a way to trick the browser into thinking the image is larger than it is? This is similar to what you can do with the picture element where with the sizes attribute you can set the intended size of the image.
http://codepen.io/anon/pen/KdaboK
<div class="one">
<img src="http://www.nwhgeopark.com/wp-content/uploads/landscape.jpg" />
</div>
<div class="one">
<img src="http://ecx.images-amazon.com/images/I/519fW0fHKbL._SY300_.jpg" />
</div>
.one {
border: 1px solid red;
float: left;
margin: 20px;
height: 500px;
width: 500px;
}
img {
}
You could use this jquery:
$(document).ready(function() {
var imgs = $('img');
imgs.each(function(){
var img = $(this);
var width = img.width();
var height = img.height();
if(width < height){
img.addClass('portrait');
}else{
img.addClass('landscape');
}
})
});
Which will add a class to images based on orientation. then add this css:
.landscape {
width:100%;
height:auto;
}
.portrait {
height:100%;
width:auto;
}
And it could behave as you are looking for:
JSFIDDLE
First Get the image width and height on the fly using jQuery and then set it by finding the multiplication factor according to the container width .....
<script>
getImageSize($('#image'), function(width, height) {
$('#info').text(width + ',' + height);
var multiplyFactor = ContainerWidth / width;
//Now set the image width and height
$(img).css({'width': width*multiplyFactor +'px',
'height':height * multiplyFactor+'px'});
});
function getImageSize(img, callback) {
var $img = $(img);
var wait = setInterval(function() {
var w = $img[0].naturalWidth,
h = $img[0].naturalHeight;
if (w && h) {
clearInterval(wait);
callback.apply(this, [w, h]);
}
}, 30);
}
</script>
I think the simplest would be to separate portrait format pictures from landscape format ones and apply a responsive rule, try this, it should work
<div class="one">
<img class="landscape" src="http://www.nwhgeopark.com/wp-content/uploads/landscape.jpg" />
</div>
<div class="one">
<img class="portrait" src="http://ecx.images-amazon.com/images/I/519fW0fHKbL._SY300_.jpg" />
</div>
.one {
border: 1px solid red;
float: left;
margin: 20px;
height: 500px;
width: 500px;
}
img.landscape {
width: 100% !important;
heigth: auto !important;
}
img.portrait {
height: 100% !important;
width: auto !important;
}

how does grid system compensate for variation in column heights?

Below I am using an extraction from a 12-grid 960px system.
<style>
body {
background: gray;
}
#container_12 {
width: 960px;
margin-left: auto;
margin-right: auto;
background: white;
overflow: hidden;
}
.column_1, .column_2, .column_3, .column_4, .column_5, .column_6, .column_7, .column_8, .column_9, .column_10, .column_11, .column_12 {
float : left;
margin-left : 10px;
margin-right : 10px;
margin-bottom: 10px;
}
.column_1 {
width : 60px;
}
.column_2 {
width : 140px;
}
.column_3 {
width : 220px;
}
.column_4 {
width : 300px;
}
.column_5 {
width : 380px;
}
.column_6 {
width : 460px;
}
.column_7 {
width : 540px;
}
.column_8 {
width : 620px;
}
.column_9 {
width : 700px;
}
.column_10 {
width : 780px;
}
.column_11 {
width : 860px;
}
.column_12 {
width : 940px;
}
</style>
<body>
<div id="container_12">
<!-- First row -->
<div class="column_1" style="height: 400px; background: red;">
</div>
<div class="column_11" style="height: 200px; background: red;">
</div>
<!-- Second row -->
<!--
This column overlaps into second row
<div class="column_1">
</div>
-->
<div class="column_5" style="height: 200px; background: green;">
</div>
<div class="column_3" style="height: 200px; background: green;">
</div>
<div class="column_3" style="height: 200px; background: green;">
</div>
</div>
</body>
The output:
http://jsfiddle.net/hnDtY/
Now let's say if I copy the two rows above to have a total of 4 rows. But I want to give the first two rows a purple background and the last two rows a white background:
http://jsfiddle.net/QZuED/
The problem with what I did above is I had to create a new div called "row" and wrap it around the two columns. This makes it inconsistent from the rest of the layout which does not have a div "row" wrapping every row. If I added a div "row" on each row and gave it a overflow: hidden property, then it will be impossible to have one column span two rows, because it would push the items in the second row down. So how do grid systems handle this situation?
Indeed, nesting the blocks inside of a div would force them to be unable to extend into the rest of the content. The answer I find most commonly used is a repeating background image instead.
body {
background: gray url(web.png) repeat-x;
}
This would also allow you to use a gradient that fades into the rest of the background. Here's your jsfiddle tweaked to reflect this.

Resources