Dynamically align item with CSS - css

I would like to align a div in the parent container by allowing the user to select the following options:
Top Left
Top Middle
Top Right
Left Middle
Centered
Right Middle
Bottom Left
Bottom Middle
Bottom Right
While I find a lot of resources how to vertically and horizontally align using flexbox, I am wondering if somebody has created a simple collection of CSS-classes I could easily apply.
Edit:
The basic markup is very simple, just a button:
<div>
<div class="btn">This is my button</div>
</div>
Now to get to "Right Middle" I'd ideally to do something like:
<div class="vertical-align-middle horizontal-align-right">
<div class="btn">This is my button</div>
</div>

You can position elements in this way by creating one container element with 3 sub-containers for each row with following css.
display: flex;
flex-direction: column;
justify-content: space-between;
This will position rows at top, middle and bottom of container. Next you can can use this css for each row and that will position divs at left, center and right of row.
display: flex;
justify-content: space-between;
body,
html {
margin: 0;
padding: 0;
}
.content {
height: 100vh;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.row {
display: flex;
justify-content: space-between;
}
.box {
border: 1px solid black;
}
<div class="content">
<div class="top row">
<div class="box">Top left</div>
<div class="box">Top center</div>
<div class="box">Top right</div>
</div>
<div class="middle row">
<div class="box">Middle left</div>
<div class="box">Middle center</div>
<div class="box">Middle right</div>
</div>
<div class="bottom row">
<div class="box">Bottom left</div>
<div class="box">Bottom center</div>
<div class="box">Bottom right</div>
</div>
</div>
If you have single element in container then you can use different combinations of align-items and justify-content to position element but that also depends on flex-direction (by default is row). So for example if you want to position element at bottom-center you can use
display: flex;
align-items: flex-end;
justify-content: center;
body,
html {
margin: 0;
padding: 0;
}
.content {
height: 100vh;
display: flex;
align-items: flex-end;
justify-content: center;
}
.btn {
border: 1px solid black;
}
<div class="content">
<div class="btn">Button</div>
</div>

Take a look at my example, you need to create classes for every position, then use a little jQuery to make it changed dynamically.
(function() {
$(".select-box").on("change", function() {
var $selected = $(this).find("option:selected").val();
$("#content").removeClass().addClass($selected);
});
})();
.select-box {
position: absolute;
top: 100px;
left: 100px;
}
#content {
display: flex;
width: 600px;
height: 400px;
padding: 20px;
}
.box {
width: 50px;
height: 50px;
background-color: fuchsia;
}
#content.top-left {
justify-content: flex-start;
align-items: flex-start;
}
#content.top-middle {
justify-content: center;
align-items: flex-start;
}
#content.top-right {
justify-content: flex-end;
align-items: flex-start;
}
#content.left-middle {
justify-content: flex-start;
align-items: center;
}
#content.centered {
justify-content: center;
align-items: center;
}
#content.right-middle {
justify-content: flex-end;
align-items: center;
}
#content.bottom-left {
justify-content: flex-start;
align-items: flex-end;
}
#content.bottom-middle {
justify-content: center;
align-items: flex-end;
}
#content.bottom-right {
justify-content: flex-end;
align-items: flex-end;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div id="content">
<div class="box"></div>
</div>
<select class="select-box">
<option value="top-left">Top Left</option>
<option value="top-middle">Top Middle</option>
<option value="top-right">Top Right</option>
<option value="left-middle">Left Middle</option>
<option value="centered">Centered</option>
<option value="right-middle">Right Middle</option>
<option value="bottom-left">Bottom Left</option>
<option value="bottom-middle">Bottom Middle</option>
<option value="bottom-right">Bottom Right</option>
</select>

The morass library might be useful.
$ mkdir test-project
$ npm init
$ npm install morass
$ edit index.html
$ serve
where index.html is:
<html>
<head>
<style>
.big { height: 200px; width: 200px; outline: 1px red solid; }
.small { height: 100px; width: 100px; outline: 1px green solid; }
</style>
<link rel='stylesheet' href='./node_modules/morass/dist/index.css'/>
</head>
<body>
<div class='big flex align-left align-bottom'>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<div class='small'></div>
</div>
</body>
</html>
You can replace align-left with align-center or align-right, and align-bottom with align-middle or align-top, etc.
This morass library is based on what are called "micro-classes". For instance, you could also add a vertical class to make the flex items go top to bottom, or for multiple items, justify to spread them out.

Related

Issue: nesting flexboxes next and on top of each other

I currently have a problem with nesting a flex box layout. I have a parent flex container and four separate containers within it, so far the other 3 column containers work correctly. However when I try to place another container underneath the main one it simply moves to the right side.
.container-wrap {
display: flex;
justify-content: center;
}
.container-leftbar {
display: flex;
height: 100%;
gap: 10px;
padding: 10px;
flex: 1;
align-items: center;
justify-content: center;
flex-direction: column;
max-width: fit-content;
}
.container-main {
display: flex;
height: 100%;
gap: 10px;
padding: 10px;
flex: 1;
align-items: center;
justify-content: center;
flex-direction: column;
max-width: fit-content;
}
.container-main2 {
display: flex;
height: 100%;
gap: 10px;
padding: 10px;
justify-content: center;
flex-direction: row;
flex-wrap: wrap;
}
.container-rightbar {
display: flex;
height: 100%;
gap: 10px;
padding: 10px;
align-items: center;
justify-content: center;
flex-direction: column;
max-width: fit-content;
}
<!--flex wrap-->
<div class="container-wrap">
<!--left column-->
<div class="container-leftbar">
<div class="1a">123</div>
<div class="1b">123</div>
<div class="1c">123</div>
</div>
<!--1 middle body-->
<div class="container-main">
<div class="2a">123</div>
<div class="2b">123</div>
</div>
<!--2 middle body-->
<div class="container-main2">
<div class="3a">123</div>
<div class="3b">123</div>
</div>
<!--right column-->
<div class="container-rightbar">
<div class="3a">123</div>
<div class="3b">123</div>
<div class="3c">123</div>
</div>
</div>
I tried to manually place the the top and left margin of the main box however I only really was able to achieve the top margin placing. I'm still somewhat new to using flex so any sort of help is appreciated.

Equal width flex columns when the container has fluid width

Consider the following HTML code:
<div class="ungrouped-ordered-item">
<div class="information-container">
<div class="originating-order-id"> #00019405 </div>
<div class="placed-by"> Placed by: 18175 </div>
</div>
<div class="indicator-container">
<div class="indicator"> Unpaid </div>
<div class="indicator" >Pending </div>
</div>
</div>
with the following css:
.ungrouped-ordered-item {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.information-container {
display: flex;
flex-direction: column;
}
.indicator-container {
display: flex;
flex-direction: row;
align-items: center;
}
.indicator-container .indicator {
display: flex;
justify-content: space-between;
align-items: center;
flex: 1;
padding: 0 10px;
border: 1px solid #2e2240;
}
The design I am aiming for, is:
to let the outermost flex containers (.ungrouped-ordered-item) children, grow as they need, and leave space between them, so that they are aligned to the left and right respectively of their containers
to make the children of the inner flex container (.indicator-container .indicator) be equal-width, by taking up the width of the wider element (in this case, it being the element with the text "Pending")
My first goal is achieved, but it seems, that even if adding flex: 1 to the .indicator containers, the browser will not correctly calculate the width of the two elements, and they will have uneven widths. I am presuming that this is because that their container, .indicator-container, has a fluid width. Am I right in this? How can I achieve my desired effect with all fluid width containers? (preferably without javascript).
Here's a fiddle also!
What is the argument against a width:50% for the .indicatorbox?
.ungrouped-ordered-item {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.information-container {
display: flex;
flex-direction: column;
}
.indicator-container {
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
}
.indicator-container > * {
border: 1px solid #2e2240;
padding: 0 10px;
flex-wrap: wrap;
}
.indicator-container .indicator {
width: 50%;
text-align: center;
}
<div class="ungrouped-ordered-item">
<div class="information-container">
<div class="originating-order-id"> #00019405 </div>
<div class="placed-by"> Placed by: 18175 </div>
</div>
<div class="indicator-container">
<div class="indicator"> Unpaid </div>
<div class="indicator" >Pending long long</div>
</div>
</div>
Changing the .indicator-container class from flex to grid and using automatic columns solves my problem. Although, it doesn't have as much coverage as flex, for my needs, this works:
.indicator-container {
display: grid;
grid-auto-columns: minmax(0, 1fr);
grid-auto-flow: column;
}
Here's the updated fiddle also.

How to control the space between flex items

I'm just doing some learning material on Codeacademy and I'm wanting to know how to control the space between the "locations" text and the three divs below. The assignment is asking me to create a 15px space between them but I don't know how to do that. Currently, there is just a default space that I don't know how is calculated.
html {
text-align: center;
}
.location-container {
background-image: url(https://content.codecademy.com/courses/freelance-1/unit-4/img-locations-background.jpg);
height: 700px;
width: 1200px;
display: flex;
justify-content: center;
align-content: center;
flex-wrap: wrap;
}
#local {
background-color: blueviolet;
}
.location-columns {
display: flex;
justify-content: center;
width: 100%;
gap: 30px;
color: white;
}
.locations {
background-color: black;
width: 300px;
}
<div class="location-container">
<h2 id="local">Locations</h2>
<div class="location-columns">
<div class="locations">
<h3>Downtown</h3>
<h5>384 West 4th St</h5>
<h5> Suite 108</h5>
<h5>Portland, Maine</h5>
</div>
<div class="locations">
<h3>East Baysuide</h3>
<h5>3433 Phisermans Avenue</h5>
<h5>(Northwest Corner)</h5>
<h5>Portland, Maine</h5>
</div>
<div class="locations">
<h3>Oakdale</h3>
<h5>515 Crescent Avenue</h5>
<h5> Second Floor</h5>
<h5>Portland, Maine</h5>
</div>
</div>
Thanks for any insights.
Heading elements come with large upper and lower margins right from the default browser styles.
You can remove those for elements inside .locations, make it a flex container and use row-gap to control vertical spacing:
html {
text-align: center;
}
.location-container {
background-image: url(https://content.codecademy.com/courses/freelance-1/unit-4/img-locations-background.jpg);
height: 700px;
width: 1200px;
display: flex;
justify-content: center;
align-content: center;
flex-wrap: wrap;
}
#local {
background-color: blueviolet;
}
.location-columns {
display: flex;
justify-content: center;
width: 100%;
gap: 30px;
color: white;
}
.locations {
background-color: black;
display: flex;
flex-direction: column;
row-gap: 30px;
padding: 30px;
width: 300px;
}
.locations>* { margin: 0; }
<div class="location-container">
<h2 id="local">Locations</h2>
<div class="location-columns">
<div class="locations">
<h3>Downtown</h3>
<h5>384 West 4th St</h5>
<h5> Suite 108</h5>
<h5>Portland, Maine</h5>
</div>
<div class="locations">
<h3>East Baysuide</h3>
<h5>3433 Phisermans Avenue</h5>
<h5>(Northwest Corner)</h5>
<h5>Portland, Maine</h5>
</div>
<div class="locations">
<h3>Oakdale</h3>
<h5>515 Crescent Avenue</h5>
<h5> Second Floor</h5>
<h5>Portland, Maine</h5>
</div>
</div>

Flexbox item vertically centered while fixing another item to bottom left corner

How can I fix the meta class to the bottom left corner of the flex container while keeping the heading element h1 vertically centered?
.container {
height: 200px;
background: red;
display: flex;
align-items: center;
}
<div class="container">
<h1>Hello, world!</h1>
<div class="meta">intro</div>
</div>
Take it out of flow and position it relatively to the container.
.container {
height: 200px;
background: red;
display: flex;
align-items: center;
position: relative;
}
.meta {
position: absolute;
bottom: 0;
left: 0;
}
<div class="container">
<h1>Hello, world!</h1>
<div class="meta">intro</div>
</div>
Switch to flex-direction: column
Apply justify-content: space-between
Insert an invisible "spacer" item to balance out both ends
.container {
height: 200px;
background: red;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.invisible { visibility: hidden; }
<div class="container">
<div class="invisible">intro</div>
<h1>Hello, world!</h1>
<div class="meta">intro</div>
</div>
Note that flex alignment properties work by distributing available space in the container. This means that justify-content: space-between can precisely center the middle item (h1) only if both adjacent items are equal height. For more details see Box #71 here.

Use justify-content: flex-end and to have vertical scrollbar

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
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.
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.
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;
}
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.
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.
This solution worked for me:
display: flex;
flex-direction: row-reverse;
justify-content: flex-start;

Resources