How to align html? - javascript

I'm using display flex to display multiple items in one big container (parentDiv). The code is working fine but I get big problems with horizontal centering the items (especially If there are only a few items they should get horizontally centered) so I was using justify-content what leads to big issues:
The parent div is not able to display all items anymore. The first item that gets displayed is the item "04" while it should be "01". How to avoid this?
Please have a look at this code:
#bigDiv {
position: absolute;
width: 100%;
height: 100%;
}
#parentDiv {
width: 90%;
height: 50%;
overflow-y: hidden;
overflow-x: scroll;
text-align: center;
white-space: nowrap;
background: red;
display: flex;
justify-content: center;
align-items: center;
}
.item {
color: white;
background: blue;
flex: 0 0 4%;
margin: 0 3%;
}
.item::after {
content: "";
display: block;
padding-bottom: 100%;
}
<div id="bigDiv">
<div id="parentDiv">
<div class="item">01</div>
<div class="item">02</div>
<div class="item">03</div>
<div class="item">04</div>
<div class="item">05</div>
<div class="item">06</div>
<div class="item">07</div>
<div class="item">08</div>
<div class="item">09</div>
<div class="item">10</div>
<div class="item">11</div>
<div class="item">12</div>
<div class="item">13</div>
<div class="item">14</div>
<div class="item">15</div>
<div class="item">16</div>
</div>
</div>
See this image:
My intentions: The parent div should be able to show all of the items (starting with "01" - and the last element should be the "16"-one)
Note: If there are only 4 or less items they should get centered horizontally. (The reason why I added justify-content).

You're fiting 160% into 100%. And you want it centered. And it works: the 160% total width of the resulting children is nicely centered.
But you're also expecting whatever is outside the parent to be accessible.
It's pretty much like making a child element go outside of its parent by -30% to the left or to the top (by any other method) and expecting the parent to allow you to scroll to it. It's not going to happen!
If it did, the child would no longer be placed at -30%, it would be placed at 0%. Scrollbars will never scroll to left or top negative space. It's by design. You need to take it into consideration when designing your page.
Whenever you center a bigger child into a smaller parent you won't be able to use parent's scrollbars to scroll to the beginning of the child. So anything preventing the child positioning in the parent's left negative space will fix it.

Related

Flexbox container resize possible? [duplicate]

I am working on a nested flexbox layout which should work as follows:
The outermost level (ul#main) is a horizontal list that must expand to the right when more items are added to it. If it grows too big, there should be a horizontal scroll bar.
#main {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
overflow-x: auto;
/* ...and more... */
}
Each item of this list (ul#main > li) has a header (ul#main > li > h2) and an inner list (ul#main > li > ul.tasks). This inner list is vertical and should wrap into columns when needed. When wrapping into more columns, its width should increase to make room for more items. This width increase should apply also to the containing item of the outer list.
.tasks {
flex-direction: column;
flex-wrap: wrap;
/* ...and more... */
}
My problem is that the inner lists don't wrap when the height of the window gets too small. I have tried lots of tampering with all the flex properties, trying to follow the guidelines at CSS-Tricks meticulously, but no luck.
This JSFiddle shows what I have so far.
Expected result (what I want):
Actual result (what I get):
Older result (what I got in 2015):
UPDATE
After some investigation, this is beginning to look like a bigger issue. All major browsers behave the same way, and it has nothing to do with my flexbox design being nested. Even simpler flexbox column layouts refuse to increase the list's width when the items wrap.
This other JSFiddle clearly demonstrates the problem. In current versions of Chrome, Firefox and IE11, all items wrap correctly; the list's height increases in row mode, but its width does not increase in column mode. Also, there is no immediate reflow of elements at all when changing the height of a column mode, but there is in row mode.
However, the official specs (look specifically at example 5) seem to indicate that what I want to do should be possible.
Can someone come up with a workaround to this problem?
UPDATE 2
After a lot of experimenting using JavaScript to update the height and width of various elements during resize events, I have come to the conclusion that it is too complex and too much trouble to try to solve it that way. Also, adding JavaScript definitely breaks the flexbox model, which should be kept as clean as possible.
For now, I'm falling back to overflow-y: auto instead of flex-wrap: wrap so that the inner container scrolls vertically when needed. It is not pretty, but it is one way forward that at least does not break useability too much.
The Problem
This looks like a fundamental deficiency in flex layout.
A flex container in column-direction will not expand to accommodate additional columns. (This is not a problem in flex-direction: row.)
This question has been asked many times (see list below), with no clean answers in CSS.
It's hard to pin this as a bug because the problem occurs across all major browsers. But it does raise the question:
How is it possible that all major browsers got the flex container to
expand on wrap in row-direction but not in column-direction?
You would think at least one of them would get it right. I can only speculate on the reason. Maybe it was a technically difficult implementation and was shelved for this iteration.
UPDATE: The issue appears to be resolved in Edge v16.
Illustration of the Problem
The OP created a useful demo illustrating the problem. I'm copying it here: http://jsfiddle.net/nwccdwLw/1/
Workaround Options
Hacky solutions from the Stack Overflow community:
"It seems this issue cannot be solved only with CSS, so I propose you a JQuery solution."
"It's curious that most browsers haven't implemented column flex containers correctly, but the support for writing modes is reasonably good. Therefore, you can use a row flex container with a vertical writing mode."
More Analysis
Chromium Bug Report
Mark Amery's answer
Other Posts Describing the Same Problem
Flex box container width doesn't grow
How can I make a display:flex container expand horizontally with its wrapped contents?
Flex-flow: column wrap. How to set container's width equal to content?
Flexbox flex-flow column wrap bugs in chrome?
How do I use "flex-flow: column wrap"?
Flex container doesn't expand when contents wrap in a column
flex-flow: column wrap, in a flex box causing overflow of parent container
Html flexbox container does not expand over wrapped children
Flexbox container and overflowing flex children?
How can I make a flexbox container that stretches to fit wrapped items?
Flex container calculating one column, when there are multiple columns
Make container full width with flex
Flexbox container resize possible?
Flex-Wrap Inside Flex-Grow
Flexbox grow to contain
Expand flexbox element to its contents?
flexbox column stretch to fit content
https://stackoverflow.com/q/48406237/3597276
flex-flow: column wrap doesn't stretch the parent element's width
Why doesn't my <ul> expand width to cover all the <li>?
https://stackoverflow.com/q/55709208/3597276
Flexbox wrap not increasing the width of parent?
Absolute Flex container not changing to the correct width with defined max-height
Late to the party, but was still running into this issue YEARS later. Ended up finding a solution using grid. On the container you can use
display: grid;
grid-auto-flow: column;
grid-template-rows: repeat(6, auto);
I have an example on CodePen that toggles between the flexbox issue and the grid fix: https://codepen.io/MandeeD/pen/JVLdLd
CSS-only workaround
Nearly 6 years after this question was asked, this flexbox bug still exists, so here's a CSS-only flex-direction: column workaround for anyone else that ends up here:
body {
background-color: grey;
}
button {
background-color: white;
border: none;
border-radius: 4px;
width: 80px;
height: 40px;
margin: 4px;
}
/* WORKAROUND FOR flex-direction: column WITH WRAP IS BELOW */
.wrapped-columns {
flex-direction: row;
flex-wrap: wrap;
writing-mode: vertical-lr;
text-orientation: upright;
}
/* Ensures content is rendered correctly in Firefox */
.wrapped-columns * {
writing-mode: horizontal-tb;
}
<div class="wrapped-columns">
<button>Button 1</button>
<button>Button 2</button>
<button>Button 3</button>
<button>Button 4</button>
<button>Button 5</button>
<button>Button 6</button>
<button>Button 7</button>
<button>Button 8</button>
<button>Button 9</button>
<button>Button 10</button>
<button>Button 11</button>
<button>Button 12</button>
<button>Button 13</button>
<button>Button 14</button>
</div>
This workaround gives the same outcome as flex-direction: column and works with both flex-wrap: wrap and wrap-reverse.
I just found a really awesome PURE CSS workaround here.
https://jsfiddle.net/gcob492x/3/
The tricky: set writing-mode: vertical-lr in the list div then writing-mode: horizontal-tb in the list item. I had to tweak the styles in the JSFiddle (remove a lot of the alignment styles, which aren't necessary for the solution).
Note: the comment says it only works in Chromium-based browsers, and not Firefox. I've only personally tested in Chrome. It's possible either there's a way to modify this to make it work in other browsers or there have been updates to said browsers that make this work.
Big shoutout to this comment: When flexbox items wrap in column mode, container does not grow its width. Digging through that issue thread led me to https://bugs.chromium.org/p/chromium/issues/detail?id=507397#c39 which led me to this JSFiddle.
It is unfortunate that so many major browsers suffer from this bug after many years. Consider a Javascript workaround. Whenever the browser window resizes, or content is added to the element, execute this code to get it to resize to the proper width. You can define a directive in your framework to do it for you.
element.style.flexBasis = "auto";
element.style.flexBasis = `${element.scrollWidth}px`;
Since no solution or proper workaround was suggested yet, I managed to obtain the requested behavior with a little different approach. Instead of separating the layout into 3 different divs, I'm adding all the items into 1 div and creating the separation with some more divs in between.
The proposed solution is hard coded, assuming we have 3 sections, but can be extended to a generic one. The main idea is to explain how we can achieve this layout.
Adding all the items into 1 container div that uses flex to wrap the items
The first item of each "inner container" (I'll call it a section) will have a class, which helps us to do some manipulations that create the separation and styling of each section.
Using :before on each first item, we can locate the title of each section.
Using space creates the gap between the sections
Since the space won't cover the full height of the section I'm also adding :after to the sections so positioning it with absolute position and white background.
To style the background color of each section I'm adding another div inside the first item of each section. I will be position with absolute as well and will have z-index: -1.
To get the correct width of each background, I'm using JS, setting the correct width, and also adding a listener to resize.
function calcWidth() {
var size = $(document).width();
var end = $(".end").offset().left;
var todoWidth = $(".doing-first").offset().left;
$(".bg-todo").css("width", todoWidth);
var doingWidth = $(".done-first").offset().left - todoWidth;
$(".bg-doing").css("width", doingWidth);
var doneWidth = $(".end").offset().left - $(".done-first").offset().left;
$(".bg-done").css("width", doneWidth + 20);
}
calcWidth();
$(window).resize(function() {
calcWidth();
});
.container {
display: flex;
flex-wrap: wrap;
flex-direction: column;
height: 120px;
align-content: flex-start;
padding-top: 30px;
overflow-x: auto;
overflow-y: hidden;
}
.item {
width: 200px;
background-color: #e5e5e5;
border-radius: 5px;
height: 20px;
margin: 5px;
position: relative;
box-shadow: 1px 1px 5px 0px rgba(0, 0, 0, 0.75);
padding: 5px;
}
.space {
height: 150px;
width: 10px;
background-color: #fff;
margin: 10px;
}
.todo-first:before {
position: absolute;
top: -30px;
height: 30px;
content: "To Do (2)";
font-weight: bold;
}
.doing-first:before {
position: absolute;
top: -30px;
height: 30px;
content: "Doing (5)";
font-weight: bold;
}
.doing-first:after,
.done-first:after {
position: absolute;
top: -35px;
left: -25px;
width: 10px;
height: 180px;
z-index: 10;
background-color: #fff;
content: "";
}
.done-first:before {
position: absolute;
top: -30px;
height: 30px;
content: "Done (3)";
font-weight: bold;
}
.bg-todo {
position: absolute;
background-color: #FFEFD3;
width: 100vw;
height: 150px;
top: -30px;
left: -10px;
z-index: -1;
}
.bg-doing {
position: absolute;
background-color: #EFDCFF;
width: 100vw;
height: 150px;
top: -30px;
left: -15px;
z-index: -1;
}
.bg-done {
position: absolute;
background-color: #DCFFEE;
width: 10vw;
height: 150px;
top: -30px;
left: -15px;
z-index: -1;
}
.end {
height: 150px;
width: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="item todo-first">
<div class="bg-todo"></div>
Drink coffee
</div>
<div class="item">Go to work</div>
<div class="space"></div>
<div class="item doing-first">
<div class="bg-doing"></div>
1
</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="space"></div>
<div class="item done-first">
<div class="bg-done"></div>
1
</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="end"></div>
</div>
I solved my issue in this manner I hope it helps someone else that stumbles on thid :
_ensureWidth(parentElement) {
let totalRealWidth = 0;
let parentElementBoundingRect = parentElement.getBoundingClientRect()
let lowestLeft = parentElementBoundingRect.x;
let highestRight = parentElementBoundingRect.width;
for (let i = 0; i < parentElement.children.length; i++) {
let { x, width } = parentElement.children[i].getBoundingClientRect();
if (x < lowestLeft) {
lowestLeft = x;
}
if (x + width > highestRight) {
highestRight = x + width;
}
}
totalRealWidth = highestRight - lowestLeft;
parentElement.style.width = `${totalRealWidth}px`;
}
Workaround :
using javascript its not hard to set the wrapper's width manually after elements have loaded on the screen. The width would always be the last child element's right hand point.
In react i have it updated on the layout changes based on any children being added to the flex wrapper , but this could be called at any point you add or remove children to the wrapper .
let r = refWrapper.current.lastElementChild.getBoundingClientRect()
refWrapper.current.style.width = (r.x+r.width )+'px'
`
where refWrapper is your your flex element
Possible JS solution..
var ul = $("ul.ul-to-fix");
if(ul.find("li").length>{max_possible_rows)){
if(!ul.hasClass("width-calculated")){
ul.width(ul.find("li").eq(0).width()*ul.css("columns"));
ul.addClass("width-calculated");
}
}

Preserve visual order of mixed floated elements

JSFiddle : https://jsfiddle.net/ttpkfs9s/
I have a UI component that should arrange elements into a row and displays them with elements on the left and on the right, with the active element being in the middle:
[1][2][3] [4] [5][6][7][8][9]
So far I have been achieving this by floating elements left and right, while keeping the one in the middle float: none; (this is good enough).
However, way too late into implementing the navigation JS I realised that I've made a huge mistake, and that the actual order the elements are displayed in are as follows:
[1][2][3] [4] [9][8][7][6][5]
Which is a huge problem as these elements are supposed to be clickable /facepalm
Are there any at most not too invasive CSS/HTML options I can use to get the displayed order correct?
EDIT: I missed the part about you needing the active div to always be in the center of the row.
You could contain the div's inside a container, and float the container insted, but that would probably be hard to do.
I took the liberty of changing things up abit, maybe you can use it, maybe u can't.
I set all items to the same width, and made a function for resizing the div's after u click one of the items.
https://jsfiddle.net/ttpkfs9s/1/
html
<div class="row">
<div class="item left">1</div>
<div class="item left">2</div>
<div class="item left">3</div>
<div class="item left">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
<div class="item">9</div>
<div class="item">10</div>
css
.row {
height: 150px;
background: blue;
width: 100%;
}
.item {
float: left;
padding: 2.5px;
color: white;
width: 9.4%;
height: 100%;
background: red;
margin: 0 0.3%;
box-sizing: border-box;
transition: 0.7s linear;
}
.active {
color: black;
background: yellow;
}
js
function setWidth(){
if($(".item").hasClass("active")){
$(".item").width("6%");
$(".active").width("40%");
};
}
$(".item").click(function(){
$(".item").removeClass("active");
$(this).addClass("active");
setWidth();
})

Make a grid of four divs expand to match the tallest height

I'm trying to create a grid layout that sizes all child divs to match the tallest div in the group. For example, I have a 2x2 grid, with each div in the grid containing some text. The div that contains the most text is tallest, and should push the height on the other 3 divs to match.
I can get the div heights to match on each row using display: flex; and flex-wrap: wrap;, but I need all rows to match in height.
Is this possible to accomplish with CSS, or will I need to resort to jQuery?
<section class="grid two">
<div>
<div>
<p>The top left div</p>
</div>
</div>
<div>
<div>
<p>The top right div</p>
</div>
</div>
<div>
<div>
<p>The bottom left div</p>
</div>
</div>
<div>
<div>
<p>The bottom right div</p>
<p>this has more text, and should casue the rest to strecth to match its height.</p>
</div>
</div>
</section>
And my current CSS is:
.grid {
display: flex;
flex-wrap: wrap;
}
.grid > div {
box-sizing: border-box;
display: flex;
flex-directon: column;
padding: 10px;
width: 50%;
}
.grid > div > div {
background: #EEE;
flex: 1 1 auto;
width: 100%;
}
Here's a fiddle: http://jsfiddle.net/g41xas6w/
Update 1: I can't used a fixed height for two reasons:
The client will be able to edit this text, so I can't always know what the height will be.
This is a responsive site, so I'd have to do a whole bunch of breaks to ensure that things are always displaying at the correct height.
http://brm.io/jquery-match-height/
Please see the link above. These guys have created a jQuery plugin to achieve the effect you are after.
Your solution would work only for the 4 DIVs in a single horizontal line. Change the width and look! If you require the 2X2, the dynamically getting the height of all 4 to be same is not possible with only CSS. You need to either use script to control it or have a height set which would stop being dynamic!
If you put a height tag in the .grid > div with for example 200px it will all be 200px
.grid > div {
box-sizing: border-box;
display: flex;
flex-directon: column;
padding: 10px;
width: 50%;
height: 200px;
}

Align div with first vertically center div

I have a page, where I'm showing images side by side according to the category they belong to, each image array begins with the category it belongs to. Images vary in their width & height, but are put into a div with an absolute height of 330px.
CSS:
.front-index {
margin: 0 1em 0 2em;
}
.front-work {
margin: 0 2em 2em 0;
display: table;
width: 66px;
position: relative;
height: 330px;
background-color:#f0f0f0;
}
.front-work img {
margin: .3em 0 .3em 0;
}
.wraptocenter {
display: table-cell;
text-align: center;
vertical-align: middle;
}
.wraptocenter * {
vertical-align: middle;
}
.front-index,
.front-work {
float: left;
}
HTML:
<div class="front-index"><p>How to get<br /> this text on the same line with<br /> yellow image on the right?</p></div>
<div class="front-work">
<div class="wraptocenter">
<img width="162" height="250" src="http://images.fanpop.com/images/image_uploads/Yellow-Wallpaper-yellow-646738_800_600.jpg"/>
</div>
</div>
<div class="front-work">
<div class="wraptocenter">
<img width="250" height="166" src="http://www.takenseriouslyamusing.com/wp-content/uploads/2012/08/Blue.png"/>
</div>
</div>
…
JSFIDDLE: http://jsfiddle.net/rCAKa/9/
I'd like to align the text to the same line as the first image on the right.
What I had in mind, is that may-be this should be done in jquery. Ie. somehow measure the image distance from the top inside the .front-work div and then assign the value to the .front-index div as an inline code (top: whatever px ).
Maybe someone of you have faced this kind of problem and know a solution to this kind of problem? CSS or JS.
In my humble opinion I don't think that what you're doing is possible through CSS - it requires some simple JavaScript trickery because you have to know the relative position (from the top of the container) of the first image on the right in order to position the text - something which CSS isn't quite designed for.
The strategy in JS would be:
Loop through each element with text that you want to position
Fetch the vertical top offset of the first image to the right (relative to containing parent)
Set top padding matching to top position of image. Alternatively, you can set the top position, paddings or margins of the child elements, or other ways to reposition the text.
$(document).ready(function() {
$(".front-index").each(function() {
var fromTop = $(this).next().find("img").position().top;
$(this).css({
"padding-top":fromTop
});
});
});
I have forked your fiddle, and you can see it in action here - http://jsfiddle.net/teddyrised/LT54V/1/
p/s: On a related note, .wraptocenter * { } is probably not the best (as in, most efficient) selector out there, because if you have many child elements in the element (who may or may have even more child elements), CSS will have to iterate through all of them. Instead, try using .wraptocenter > * { } or just .wraptocenter img { } :)
I first tried to solve the problem using css. After a while I figured out the following logics:
Create a div with the same height as the cell on the right with the display set as table
Make a table-cell div in the first one that centers vertically
In this div make another subdiv with the same height as the image.
The HTML code is then this:
<div class="front-index">
<div class="front-index-inner">
<div style="height:250px;">
<p>How to get<br /> this text on the same line with<br /> yellow image on the right?</p>
</div>
</div>
</div>
and as my CSS part this:
.front-index {
margin: 0 1em 0 2em;
display: table;
height: 330px;
overflow: hidden;
}
.front-index-inner {
display: table-cell;
width: 100%;
vertical-align: middle;
}
You can see the result over here: http://jsfiddle.net/rCAKa/10/
I hope this brings a solution to you that is clear, understandable and useful.
Greetings,
Jef

How to stack divs beside each other to create a carousel

I am trying to create a carousel, where clicking on any element will slide it leftwards, simultaneously sliding the right element into viewport. For that, I need to have the divs stacked side by side. I am trying it out as a float based layout (see Fiddle ).
Problem is that here clicking the red colored div slides it leftward alright, but not the green element leftwards. This is probably due to the fact that they are actually lying below another, as visible when the overflow: hidden is removed from #cont's style. How elese to stack them side by side so that sliding one leftward automatically slides the next one leftwards as well? (Creating the to-be-next element on the fly while clicking and animating it into viewport is a no-no, the element should be present in the DOM!)
I'd suggest you use a plugin, as there is more to this than you may realize. There are many plugins out there for this, here's a list to get you started: http://www.tripwiremagazine.com/2012/12/jquery-carousel.html
I modified your Javascript, HTML, and CSS to get you pointed in the right direction:
http://jsfiddle.net/nf5Dh/2/
You need a container contContent, positioned absolutely, and that container gets moved within the container div. You just float the elements in contContent to get them next to each other.
HTML:
<div id='cont'>
<div id="contContent">
<div id='i1'></div>
<div id='i2'></div>
<div id='i3'></div>
</div>
</div>
CSS:
#cont {
width: 50px;
padding-top: 10px;
background: blue;
height: 50px;
overflow: hidden;
position: relative;
}
#contContent {
height: 50px;
width: 150px;
position: absolute;
top: 0;
left: 0;
}
#contContent > div {
float: left;
display: inline-block;
height: 50px;
width: 50px;
}
#i1 { background: red; }
#i2 { background: green; }
#i3 { background: yellow; }
And the JS:
$("#contContent > div").click(function(){
$("#contContent").animate({left: "-=50px"},1000);
});
You'd probably be better off using an ul instead of all divs, this is at least more semantically correct, though not technically necessary.
<div id="carousel">
<ul id="carouselContent">
<li id="slide1"></li>
<li id="slide2"></li>
<li id="slide3"></li>
</ul>
</div>
This:
#cont {
white-space:nowrap;
overflow: hidden;
}
.pane { // or whatever the slide divs are called. get rid of the float.
float: none;
display: inline-block;
*zoom:1;
*display:inline;
}
You can use that carousel where you can generate javascript for the carousel http://caroufredsel.dev7studios.com/configuration-robot.php
I've used http://sorgalla.com/jcarousel/ for things like this in the past, that's based on postion: relative and left/right offsets. Probably easier than messing with floats.
You can try using a list item instead, and display them inline.

Categories