css flex column auto height - javascript

I am creating a mega menu trying to keep to current standards; CSS, jquery, bootstrap.
Google Images - Mega Menu
My menu is for product categories, which I want dynamically created in columns newspaper style. Easy enough, I have that working nicely. It requires a fixed height, which makes it overflow horizontally.
My current thinking is to, onload, increase the heights of each sub menu until there is no horizontal overflow. I can get it working with some manual intervention, which defeats to purpose of a dynamic layout.
.container {
width: 800px;
height: 100px;
display: flex;
flex: 1 1 auto;
}
.container .wrap {
display: flex;
flex: 1 1 auto;
flex-flow: column wrap;
align-content: flex-start;
}
.container .wrap .item {
width: 150px;
}
<div class="container">
<div class="wrap">
<div class="item">Item 1<br>a<br>b</div>
<div class="item">Item 2<br>a</div>
<div class="item">Item 3<br>a<br>b<br>c<br>d</div>
<div class="item">Item 4<br>a</div>
<div class="item">Item 5<br>a</div>
</div>
</div>
This style works perfectly aligning items top to bottom, left to right, as required.
Now I am trying to increase the height of each sub menu as required. Best solution is to detect width overflow and increase height to compensate.
I've tried native JS and jQuery, I seem to be getting stuck running it in a function. I can detect overflow and increase height, as my code below. What I want is to have the if statements in a loop and exit when no more overflow, and end up with a perfect fit.
EDIT
Current working solution
$(function () {
$(".container").each( function( index, element ){
var my_height = $(element).outerHeight();
if( $(element).prop('scrollWidth') > $(element).outerWidth() ) {
while( $(element).prop('scrollWidth') > $(element).outerWidth() )
{
my_height += 100;
$(element).css('height', my_height + "px");
}
}
});
});
Works exactly as intended.
EDIT
Updated question. Is there a better solution? Something that doesn't involve looping over every item every page. Pure CSS would be nice, but don't think it's possible just yet.
Solved my transition issue. When trying to update the height in a loop as above, transition CSS interferes and creates an infinite loop. Was an easy fix, I added a new class to .container .transition and added $(element).removeClass("transition"); to the start or the loop and $(element).addClass("transition"); to the end. This removes the transition CSS from the inner loop and adds it back at the end.
FINAL EDIT
My first attempt was using css columns, but I couldn't get it working to my spec. It wasn't until reading the accepted answer below I revisited columns and after some testing got it working.
Ref https://developer.mozilla.org/en-US/docs/Web/CSS/columns

There is a way of doing this using pure CSS using the column CSS properties:
Base CSS:
.container {
width: 800px;
}
.container .wrap {}
.container .wrap .item {
width: 150px;
}
To have columns of a fixed width:
.container .wrap {
column-width: 125px;
}
To have a set number of columns:
.container .wrap {
column-count: 5;
}
JSFiddle
Another answer giving details about browser support

Related

Problem with nested flex containers. Inner container's item is not respecting its parent's width [duplicate]

I have 4 flexbox columns and everything works fine, but when I add some text to a column and set it to a big font size, it is making the column wider than it should be due to the flex property.
I tried to use word-break: break-word and it helped, but still when I resize the column to a very small width, letters in the text are broken into multiple lines (one letter per line), and yet the column does not get smaller width than one letter size.
Watch this video
(at the start, the first column is the smallest, but when I resized the window, it is the widest column. I just want to respect flex settings always; flex sizes 1 : 3 : 4 : 4)
I know, setting font-size and column padding to smaller will help... but is there any other solution?
I can not use overflow-x: hidden.
JSFiddle
.container {
display: flex;
width: 100%
}
.col {
min-height: 200px;
padding: 30px;
word-break: break-word
}
.col1 {
flex: 1;
background: orange;
font-size: 80px
}
.col2 {
flex: 3;
background: yellow
}
.col3 {
flex: 4;
background: skyblue
}
.col4 {
flex: 4;
background: red
}
<div class="container">
<div class="col col1">Lorem ipsum dolor</div>
<div class="col col2">Lorem ipsum dolor</div>
<div class="col col3">Lorem ipsum dolor</div>
<div class="col col4">Lorem ipsum dolor</div>
</div>
The Automatic Minimum Size of Flex Items
You're encountering a flexbox default setting.
A flex item cannot be smaller than the size of its content along the main axis.
The defaults are...
min-width: auto
min-height: auto
...for flex items in row-direction and column-direction, respectively.
You can override these defaults by setting flex items to:
min-width: 0
min-height: 0
overflow: hidden (or any other value, except visible)
Flexbox Specification
4.5. Automatic Minimum Size of Flex
Items
To provide a more reasonable default minimum size for flex items, this
specification introduces a new auto value as the initial value of
the min-width and min-height properties defined in CSS 2.1.
With regard to the auto value...
On a flex item whose overflow is visible in the main axis, when specified on the flex item’s main-axis min-size property, specifies an automatic minimum size. It otherwise computes to 0.
In other words:
The min-width: auto and min-height: auto defaults apply only when overflow is visible.
If the overflow value is not visible, the value of the min-size property is 0.
Hence, overflow: hidden can be a substitute for min-width: 0 and min-height: 0.
and...
The minimum sizing algorithm applies only on the main axis.
For example, a flex item in a row-direction container does not get min-height: auto by default.
For a more detailed explanation see this post:
min-width rendering differently in flex-direction: row and flex-direction: column
You've applied min-width: 0 and the item still doesn't shrink?
Nested Flex Containers
If you're dealing with flex items on multiple levels of the HTML structure, it may be necessary to override the default min-width: auto / min-height: auto on items at higher levels.
Basically, a higher level flex item with min-width: auto can prevent shrinking on items nested below with min-width: 0.
Examples:
Flex item is not shrinking smaller than its content
Fitting child into parent
white-space css property is creating issues with flex
Browser Rendering Notes
Chrome vs. Firefox / Edge
Since at least 2017, it appears that Chrome is either (1) reverting back to the min-width: 0 / min-height: 0 defaults, or (2) automatically applying the 0 defaults in certain situations based on a mystery algorithm. (This could be what they call an intervention.) As a result, many people are seeing their layout (especially desired scrollbars) work as expected in Chrome, but not in Firefox / Edge. This issue is covered in more detail here: flex-shrink discrepancy between Firefox and Chrome
IE11
As noted in the spec, the auto value for the min-width and min-height properties is "new". This means that some browsers may still render a 0 value by default, because they implemented flex layout before the value was updated and because 0 is the initial value for min-width and min-height in CSS 2.1. One such browser is IE11. Other browsers have updated to the newer auto value as defined in the flexbox spec.
Revised Demo
.container {
display: flex;
}
.col {
min-height: 200px;
padding: 30px;
word-break: break-word
}
.col1 {
flex: 1;
background: orange;
font-size: 80px;
min-width: 0; /* NEW */
}
.col2 {
flex: 3;
background: yellow
}
.col3 {
flex: 4;
background: skyblue
}
.col4 {
flex: 4;
background: red
}
<div class="container">
<div class="col col1">Lorem ipsum dolor</div>
<div class="col col2">Lorem ipsum dolor</div>
<div class="col col3">Lorem ipsum dolor</div>
<div class="col col4">Lorem ipsum dolor</div>
</div>
jsFiddle
I'm finding this has bitten me repeatedly over the years for both flex and grid, so I'm going to suggest the following:
* { min-width: 0; min-height: 0; }
and then just use min-width: auto or min-height: auto if you need that behaviour.
In fact, throw in box-sizing as well to make all layout more sane:
* { box-sizing: border-box; min-width: 0; min-height: 0; }
Does anyone know if there are any odd consequences? I've not encountered anything in several years of using a mix of the above. In fact, I can't think of any cases where I'd want to layout from content outwards to the flex/grid, rather than flex/grid inwards to the content --- and surely if they exist, they're rare. So this feels like a bad default. But maybe I'm missing something?
The pure answer to your question is that by default, browsers tend to display as much information as possible to the reader (and not to hide anything).
That happens by default, and even includes showing default black color fonts on a white background (for maximum page contrast and readability), adding a scroll bar where content is larger than the viewport height (or width) or still showing content from a markup (or the background color) even if this was mistakenly placed after </body> or even </html> tags in the html file.
In context of CSS, this applies as well, but you also are allowed to play with many customizations on top of that.
Even in a screen if using a huge font (like font-size: 50em;) this initially acts as an overflowing element (and placing the font inside a flexible child container by using display: flex doesn't change this default behaviour unless you use overflow: hidden or resize the element in some way.
An elegant solution is to use a dynamic resizing of the letters, for example
font-size: calc(0.5em + 2vw)
which works great even in a responsive scenario.
As a previous answer mentioned, A flex item cannot be smaller than the size of its content along the main axis (for the same reason, that is not only specific to the flexbox model implemented in CSS but because of the inner browser way of working). Even a long word is displayed with a scrollbar if it's longer than display width as if being a block type element with a fixed size instead.
This is mentioned in old html 4.01 specifications as
"By convention, visual HTML user agents wrap text lines to fit within
the available margins. Wrapping algorithms depend on the script being
formatted.
In Western scripts, for example, text should only be wrapped at white
space. "
as seen here in paragraph 9.5.3. This means that, since then, the text had to be continuously displayed by default (unless we decide to split it but not at single character level: a single non-white character shown at 120em size will trigger scrollbars displaying on the browser).
Words are also clearly defined in paragraph 9.1 in the same source:
we use the term "word" here to mean "sequences of non-white space
characters"
The purpose of displaying the original format of any word is to not destroy, hide or distort the original information, the meaning or intent of the code author. As such, we also have for keeping in same line two words that are connected - when breaking them might be disruptive (such as New York, 10 PM, 10 km/h, § 10, etc)
For this code below, adding width: 100% solved my problem.
.post-cover .inner {
display: flex;
flex-direction: column;
flex-wrap: wrap;
justify-content: center;
align-content: flex-start;
align-items: flex-start;
word-break: break-all;
z-index: 21;
}
.post-cover .article-page {
padding: 20px 0;
margin-bottom: 40px;
font-size: 0.875em;
line-height: 2.0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%; /* Add this */
}
I tried everything, even putting the below code in the index.css.
* {
min-width: 0;
min-height: 0;
box-sizing: border-box;
}
But nothing worked.
finally I made the div I wanted to shrink past it's content to have position: absolute;. Then it started shrinking.
It's parent div would need a defined height and width. This might not be the best solution for every scenario but if this works for you, good!

Nested divs with flexDirection: column; show vertical scroll bar on inner div [duplicate]

I have 4 flexbox columns and everything works fine, but when I add some text to a column and set it to a big font size, it is making the column wider than it should be due to the flex property.
I tried to use word-break: break-word and it helped, but still when I resize the column to a very small width, letters in the text are broken into multiple lines (one letter per line), and yet the column does not get smaller width than one letter size.
Watch this video
(at the start, the first column is the smallest, but when I resized the window, it is the widest column. I just want to respect flex settings always; flex sizes 1 : 3 : 4 : 4)
I know, setting font-size and column padding to smaller will help... but is there any other solution?
I can not use overflow-x: hidden.
JSFiddle
.container {
display: flex;
width: 100%
}
.col {
min-height: 200px;
padding: 30px;
word-break: break-word
}
.col1 {
flex: 1;
background: orange;
font-size: 80px
}
.col2 {
flex: 3;
background: yellow
}
.col3 {
flex: 4;
background: skyblue
}
.col4 {
flex: 4;
background: red
}
<div class="container">
<div class="col col1">Lorem ipsum dolor</div>
<div class="col col2">Lorem ipsum dolor</div>
<div class="col col3">Lorem ipsum dolor</div>
<div class="col col4">Lorem ipsum dolor</div>
</div>
The Automatic Minimum Size of Flex Items
You're encountering a flexbox default setting.
A flex item cannot be smaller than the size of its content along the main axis.
The defaults are...
min-width: auto
min-height: auto
...for flex items in row-direction and column-direction, respectively.
You can override these defaults by setting flex items to:
min-width: 0
min-height: 0
overflow: hidden (or any other value, except visible)
Flexbox Specification
4.5. Automatic Minimum Size of Flex
Items
To provide a more reasonable default minimum size for flex items, this
specification introduces a new auto value as the initial value of
the min-width and min-height properties defined in CSS 2.1.
With regard to the auto value...
On a flex item whose overflow is visible in the main axis, when specified on the flex item’s main-axis min-size property, specifies an automatic minimum size. It otherwise computes to 0.
In other words:
The min-width: auto and min-height: auto defaults apply only when overflow is visible.
If the overflow value is not visible, the value of the min-size property is 0.
Hence, overflow: hidden can be a substitute for min-width: 0 and min-height: 0.
and...
The minimum sizing algorithm applies only on the main axis.
For example, a flex item in a row-direction container does not get min-height: auto by default.
For a more detailed explanation see this post:
min-width rendering differently in flex-direction: row and flex-direction: column
You've applied min-width: 0 and the item still doesn't shrink?
Nested Flex Containers
If you're dealing with flex items on multiple levels of the HTML structure, it may be necessary to override the default min-width: auto / min-height: auto on items at higher levels.
Basically, a higher level flex item with min-width: auto can prevent shrinking on items nested below with min-width: 0.
Examples:
Flex item is not shrinking smaller than its content
Fitting child into parent
white-space css property is creating issues with flex
Browser Rendering Notes
Chrome vs. Firefox / Edge
Since at least 2017, it appears that Chrome is either (1) reverting back to the min-width: 0 / min-height: 0 defaults, or (2) automatically applying the 0 defaults in certain situations based on a mystery algorithm. (This could be what they call an intervention.) As a result, many people are seeing their layout (especially desired scrollbars) work as expected in Chrome, but not in Firefox / Edge. This issue is covered in more detail here: flex-shrink discrepancy between Firefox and Chrome
IE11
As noted in the spec, the auto value for the min-width and min-height properties is "new". This means that some browsers may still render a 0 value by default, because they implemented flex layout before the value was updated and because 0 is the initial value for min-width and min-height in CSS 2.1. One such browser is IE11. Other browsers have updated to the newer auto value as defined in the flexbox spec.
Revised Demo
.container {
display: flex;
}
.col {
min-height: 200px;
padding: 30px;
word-break: break-word
}
.col1 {
flex: 1;
background: orange;
font-size: 80px;
min-width: 0; /* NEW */
}
.col2 {
flex: 3;
background: yellow
}
.col3 {
flex: 4;
background: skyblue
}
.col4 {
flex: 4;
background: red
}
<div class="container">
<div class="col col1">Lorem ipsum dolor</div>
<div class="col col2">Lorem ipsum dolor</div>
<div class="col col3">Lorem ipsum dolor</div>
<div class="col col4">Lorem ipsum dolor</div>
</div>
jsFiddle
I'm finding this has bitten me repeatedly over the years for both flex and grid, so I'm going to suggest the following:
* { min-width: 0; min-height: 0; }
and then just use min-width: auto or min-height: auto if you need that behaviour.
In fact, throw in box-sizing as well to make all layout more sane:
* { box-sizing: border-box; min-width: 0; min-height: 0; }
Does anyone know if there are any odd consequences? I've not encountered anything in several years of using a mix of the above. In fact, I can't think of any cases where I'd want to layout from content outwards to the flex/grid, rather than flex/grid inwards to the content --- and surely if they exist, they're rare. So this feels like a bad default. But maybe I'm missing something?
The pure answer to your question is that by default, browsers tend to display as much information as possible to the reader (and not to hide anything).
That happens by default, and even includes showing default black color fonts on a white background (for maximum page contrast and readability), adding a scroll bar where content is larger than the viewport height (or width) or still showing content from a markup (or the background color) even if this was mistakenly placed after </body> or even </html> tags in the html file.
In context of CSS, this applies as well, but you also are allowed to play with many customizations on top of that.
Even in a screen if using a huge font (like font-size: 50em;) this initially acts as an overflowing element (and placing the font inside a flexible child container by using display: flex doesn't change this default behaviour unless you use overflow: hidden or resize the element in some way.
An elegant solution is to use a dynamic resizing of the letters, for example
font-size: calc(0.5em + 2vw)
which works great even in a responsive scenario.
As a previous answer mentioned, A flex item cannot be smaller than the size of its content along the main axis (for the same reason, that is not only specific to the flexbox model implemented in CSS but because of the inner browser way of working). Even a long word is displayed with a scrollbar if it's longer than display width as if being a block type element with a fixed size instead.
This is mentioned in old html 4.01 specifications as
"By convention, visual HTML user agents wrap text lines to fit within
the available margins. Wrapping algorithms depend on the script being
formatted.
In Western scripts, for example, text should only be wrapped at white
space. "
as seen here in paragraph 9.5.3. This means that, since then, the text had to be continuously displayed by default (unless we decide to split it but not at single character level: a single non-white character shown at 120em size will trigger scrollbars displaying on the browser).
Words are also clearly defined in paragraph 9.1 in the same source:
we use the term "word" here to mean "sequences of non-white space
characters"
The purpose of displaying the original format of any word is to not destroy, hide or distort the original information, the meaning or intent of the code author. As such, we also have for keeping in same line two words that are connected - when breaking them might be disruptive (such as New York, 10 PM, 10 km/h, § 10, etc)
For this code below, adding width: 100% solved my problem.
.post-cover .inner {
display: flex;
flex-direction: column;
flex-wrap: wrap;
justify-content: center;
align-content: flex-start;
align-items: flex-start;
word-break: break-all;
z-index: 21;
}
.post-cover .article-page {
padding: 20px 0;
margin-bottom: 40px;
font-size: 0.875em;
line-height: 2.0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%; /* Add this */
}
I tried everything, even putting the below code in the index.css.
* {
min-width: 0;
min-height: 0;
box-sizing: border-box;
}
But nothing worked.
finally I made the div I wanted to shrink past it's content to have position: absolute;. Then it started shrinking.
It's parent div would need a defined height and width. This might not be the best solution for every scenario but if this works for you, good!

Setting a max-height of page-height for a dropdown

Say you have a dropdown with a lot of options that overflow the page height. I know I can use overflow: auto to make it scroll, but only if I set it a max-height. How do I set a max-height that ensures the element won't overflow the browser window?
Like in this image
The left is what it's like now. Dropdown overflows page. The right is what it should be like -- the dropdown is resized to be height of just under the page height.
I've tried setting max-height to different values like 45vh since the dropdown is about halfway down the page, but this needs to fit all types of screen sizes so isn't flexible enough.
CSS solutions preferred in this case.
You can calculate the current distance between the dropdown and the bottom of the page (https://stackoverflow.com/a/7656176/5370933) and append styles with this value.
.myDropdown {
max-height: myDistance;
overflow: scroll
}
I think something like that could works. But you will have to use some JS to get the distance dynamically (depend on the user screen and/or user scroll before the dropdown opening...)
If I understood correctly the layout of your web page, the dropdown is the last element (well maybe) in the page.
What you could do is, first, add this lines to your main page container:
#page {
min-height: 100vh; /* Or the value you like most */
}
Now we have access to the full height of the document.
Next, you can simply use flexbox's space-between or space-around value to keep the dropdown on the bottom of the page (like footers).
But now, you want a little space between the end of the page and the dropdown. Simply add a margin-bottom and its done.
Now be aware that, I understand that there may be a footer or something below the dropdown. You can implement this solution in any container.
This isn't a bug-free solution, but it doesn't require javascript.
Here is a working example.
function _test_add(){
document.getElementById("dropdown").innerHTML += "<li>Item</li>";
}
#page {
min-height:100vh;
display:flex;
flex-direction: column;
justify-content: space-between;
}
#addbtn {
margin:0 auto;
}
/*
* Fictif Content
*/
#main-content {
height: 50vh;
display:flex;
justify-content:center;
align-items:center;
background-color:gray;
}
#dropdown {
min-height: 8em;
max-height: 18em;
background-color:#f1f1f1;
padding: 0;
list-style-type: none;
overflow: auto;
margin-bottom:4em;
border: solid black 2px;
}
#dropdown li {
padding:1em;
}
#dropdown li:nth-child(odd) {
background-color: #fafafa;
}
<div id="page">
<div id="main-content">
Main Content
</div>
<button id="addbtn" onclick="_test_add()">[TEST] Add items in dropdown</button>
<ul id="dropdown">
<li>Item</li>
<li>Item</li>
<li>Item</li>
</ul>
</div>

Single window application with 100% height and no scrollbars

I am currently developing a web application using jQuery.
The layout for the same goes as shown in the figure given below:
The orange color box at the very back should be 100% in height and width with some margin like 5px or so.
The logo and the tab-bar are placed as shown and are about 50px in height. But tab-bar should take size as shown with some margin.
The tab content should occupy the remaining height and should scroll for the contents it occupies.
Similar structure is required for the internal menubar and tab content.
Can anyone please suggest the layout method to employ?
Or how can I manipulate different heights/widths?
The requirement also suggests a responsive window i.e. the width/height to manipulate on resize.
The jsFiddle I said I'd make.
As you'll see, I make use of jQueryUI for the "tabs" layout and simply "add" a few things. The few things I "Add" are simple and the jQueryUI alreqady provides a strong CSS with which to manipulate to get desired result. Also attached to that page is a theme-switcher, so you could see what it would look like using different jQueryUI Default Themes.
I'll try to explain the process as shortly as possible without being to vague.
HTML
I first start with a basic page wrapper. Not too necessary, but it provides a nice "element" with which to work inside of and possibly make manipulations for page layout change in otherways in the future. For now it simply holds our page "padding" of 5px. The HTML and BODY tags will be set to a default and should not be manipulated beyond that as height and other properties begin to take different meanings for these tags in different browsers.
I then place 2 divs inside this wrapper, again, these could be done without depending on your needs. I like these 2 divs and use this alot because it provides "vertical align -> middle" as one might expect. The first, parent, is a div with class table. This will have its display set to table to provide a "table-like" layout but still have the ability to do things like "round the corners" or, as in my case, set height! The second, child, is the same except it will have a class and style as table-cell, respectively. This allows us to set something like vertical-align: middle; and ensure that this element is in the vertical middle of the page/table element. Again, with your layout, this may seem unneccessary, but I don't know your full expected end result and I'm trying to give as much "fluid dynamics" to the page as possible.
Finally, I first insert the jQueryUI tabs HTML in their expected layout, with 2 small differences. I place our "logo" in a custom span tag just before the ul. I also take the ui-tab-panel(s) and place them in their own container. This helps us adjust the height of our tabs area as needed. I also gave this container overflow, so even tho overflow maybe hidden on the body, it's still available for the tabs. (see also: small blog i wrote on jQueryUI Tabs)
<div class="page-wrapper">
<div class="table">
<div class="table-cell">
<div id="tabs">
<span class="my-logo">
<img src="http://www.w3.org/html/logo/downloads/HTML5_Logo_512.png" alt="logo here" />
</span>
<ul>
<li>Nunc tincidunt</li>
<li>Proin dolor</li>
<li>Aenean lacinia</li>
</ul>
<div class="ui-tabs-panel-container">
<div id="tabs-1">
<<p> ... </p>
</div>
<div id="tabs-2">
<p> ... </p>
</div>
<div id="tabs-3">
<p> ... </p>
</div>
</div>
</div>
</div>
</div>
</div>
CSS
As I mentioned before, jQueryUI provides us with a strong CSS to work with already. As you might have noticed, I made use of some of this by using their predefined class names throughout the HTML. This established things like background, color, and even font-family and more! Now that that is over with, let's layout our page mechanics first. As I mentioned, I give a very "direct" set of properties to HTML and BODY. This will help eliminate "Cross-browser-issues". I also provided a background color, tho you could set that at one of the children levels. This was done just to show you where HTML, BODY exist.
I then set our "frame" elements. .page-wrapper will provide our page wrapping, sizing will come from within, so there is no need to deal with it here. The .table and .table-cell provide display exactly as their name suggest. As previously mentioned, this provides a nice ability to maintain an element in the exact "center" of something, even vertically!
Now we manipulate our tabs and content. I use #tabs throughout to maintain "name-spacing". This will not only help with any "css overrides" on jQueryUI presets, but also helps keep page layout confusions to a minimum. This is always a good thing.
The first thing I manipulate is the placement and setting of our custom span for the logo. Then, of course, I have to change the ul to next to it. Thus I look at the CSS for the uls class. If I open edit tools in a browser, I can see the ul is given the classname ui-tabs-nav and I can see it has a margin setting. If I play with the margin-left of this ul I can see that nothing is affected but the left side of the ul. PERFECT! Here is what I must manipulate to set our log in its "own space".
Finally, I simply set our tabs container (given custom class name, ui-tabs-panel-container, made to match jQueryUI) to have overflow, so that if any content exceeds our page height, it can still be scrolled within this element.
html, body {
background-color: #ADDFFF;
height: 100%;
padding: 0;
margin: 0;
overflow: hidden;
width: 100%;
}
.page-wrapper {
padding: 5px;
}
.table { display: table; }
.table-cell { display: table-cell; vertical-align: middle; }
#tabs .my-logo {
display: inline-block;
float: left;
height: 2em;
margin: .5em 0 0;
padding: 0;
width: 2em;
}
#tabs .my-logo img {
max-height: 100%;
max-width: 100%;
float: left;
}
#tabs .ui-tabs-nav {
margin-left: 2em;
}
#tabs .ui-tabs-panel-container {
overflow: auto;
}
JS
Finally, the easy work. I write a function to set the height of our tabs content area, since it will be "filling" the rest of the page. This take a little thought, but not hard to figure out. With the function written, I simply add it to the window resize event and call that event right after. This way it's resized on load, thus giving us our "end height" for first view. I also establish the tabs, although not much work there since I'm just making "default tabs". Feel free to experiment, go wild!
// the following will resize our tabs content area and account for all the spacing neccessary
function setContentHeight(e) { return $(window).innerHeight() - $(this).offset().top - 10; } // -10 to account for padding
$(function() { // our on page load call
$("#tabs").tabs(); // establish tabs
// add ability to resize tabs content area on window resize, then call resize event
$(window).resize(function(e) { $("#tabs .ui-tabs-panel-container").height(setContentHeight) }).resize();
})
As for the layout of tab content, it's all up to you and your imagination. Hopefully this will give you a good idea of where to get started though! Good luck!
You could use something like Blueprint CSS:
http://www.blueprintcss.org/
Here's a very quick and dirty layout (not using blueprint CSS, just plain CSS), as a general guideline. It still needs work, but it could be used as a starting point:
<html>
<head>
<style>
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden; /* hide page scrollbars */
}
div {
margin: 0 auto;
padding: 0;
border: 1px solid black; /* for debugging */
text-align: center;
}
#header {
width: 100%;
position: relative;
overflow: auto;
}
#header > div {
height: 5%;
float: left;
}
#logo {
width: 23%;
}
#spacer {
width: 1%; /* -1% for borders */
}
#tabbar {
width: 75%;
}
#tabContent {
}
#tabContent > div {
width: 100%;
}
#tabContentMenuBar {
height: 5%;
}
#tabContentMain {
min-height: 80%;
}
</style>
</head>
<body>
<div id="header">
<div id="logo">Logo</div>
<div id="spacer"></div>
<div id="tabbar" class="fullWidth">Tab bar</div>
</div>
<div id="tabContent">
Tab content
<div id="tabContentMenuBar">Tab content - menu bar</div>
<div id="tabContentMain">Tab content - main content</div>
</div>
</body>
</html>

Distribute elements evenly using CSS

A method to distribute elements evenly in a container using CSS appeared on Smashing Magazine today.
I recently had to use Javascript to achieve the same effect for elements of variable width, but the method presented on SM made me wonder if it was possible to do this without Javascript.
There's this question, where gargantaun says:
IMHO, and you probably don't want to hear this, but the design is probably flawed. It's common knowledge that distributing items evenly across a layout with CSS is a pain, so designers should avoid it.
But I can't tell the designer to change his design, and I don't agree that the shortcomings of CSS should limit designers.
Anyway, here's what I have in HTML (translated and simplified):
<div id="menu">
<ul>
<li>Home</li>
<li>News</li>
<li>Theme</li>
<li>Activities</li>
<li>Contact</li>
</ul>
</div>
in CSS (irrelevant properties removed and simplified):
#menu li { float: left; margin-right: 20px; }
#menu a { display: block; padding: 0 1em; }
and in Javascript:
function justifyMenu() {
var menuItems = $$("#menu li");
var menuWidth = $("menu").getWidth();
var totalWidth = 0;
menuItems.each(function(e) {
totalWidth += e.getWidth();
});
var margin = (menuWidth - 4 - totalWidth) / (menuItems.length - 1);
margin = parseInt(margin);
menuItems.each(function(e) {
e.setStyle({ marginRight: margin + 'px' });
});
menuItems[menuItems.length - 1].setStyle({ marginRight: '0' });
}
And here's a scaled-down screenshot (see how the menu aligns with the header image):
Any idea how I can achieve this without Javascript?
Of course this is exactly what the table element is for. It's sad and hilarious at the same time to see people twist themselves into a gordian knot with CSS, most of them not even knowing why they're avoiding tables.
Whatever reason you might have dreamed up to reject tables, it can't possibly be worse than depending on Javascript to layout your page.
Yes, I know this is not the answer you were looking for, but golly, it's so obvious.
There have been casual claims that tables are the obvious solution, however, there hasn't been any real discussion of how to implement it. I'll show you that displaying divs as a table is the right way to do this, but it is not as easy as centering all of the cells and setting an automatic width. The problem with this is that you have no control of the outer margins of the further-most left and right cell-contents. They both are inset from its containing box an arbitrary amount you cannot control. Here's a work around:
First, a slight modification of Guder's html:
<div id="menu">
<ul>
<li class="left">Home</li>
<li>News</li>
<li>Theme</li>
<li>Activities</li>
<li>Contact</li>
</ul>
</div>
Now the css:
#menu {display:table; width:// some width in px //}
#menu ul {display:table-row; width: 100%}
#menu li.left {display: table-cell; text-align: left; width: 20px; white-space:nowrap;}
#menu li {display: table-cell; text-align: right; width: auto;}
Now, we have full control of the outer-most sides of the menu, which align with the far-left and far-right sides of the containing box, and the distance between each element is consistent. You'll notice that I used a trick to get the furthest left cell to be the exact-width of it's content. I set the width property to a small size obviously below what its contents would normally be. I then set the white-space to no-wrap, which stretches the cell the least amount to fit the text of the cell. You can see here an image which shows the effect of this (using different html elements):
The beauty of this code is that it can accept however many cells and text-widths, without any knowledge of their actual widths, and distribute them evenly across the page. All the while, left and right elements reaching their perimeters, and ofcourse we have all our html in divs, no browser or internet geek is mislead to believe we're presenting tabular data. No known compromises here!
This is what display:table-cell is supposed to achieve - however, the IE's just don't do it, and FF<3 has problems with it too, I believe.
These styles work in FF3, Chrome, Safari, and (I think) Opera 9:
#menu ul {display:table;padding:0;}
#menu li {display:table-cell;text-align:center;}
But you'll need a fair few hacks to get them working in the usual, commercial set of browsers.
Even though Colin Brogan's answer provides solid foundation to approach a "almost there" resolution to the problem, it still depends on text length. If text is too long, the "cell" will be wider and thus have more space on the left. I tried to address the problem based on the code presented in his answer, but I concluded that the problem has not a real possible solution with tables or fake-tables (display:table-cell).
So we'll have to wait for CSS3 flexible box model to be more widely supported (you can check updated support here). In the meantime, you can use the Flexie polyfill to patch browsers that don't support it.
If you want to check how it'll look like on WebKit now (without needing polyfill), you can try the following CSS:
#menu ul {
display: -webkit-box;
-webkit-box-orient: horizontal;
-webkit-box-pack: justify;
width: 940px;
list-style: none;
padding: 0;
border: 1px solid gray;
}
#menu li {
border: 1px solid silver;
}
Notice it only uses WebKit prefixes. You should add prefixes for the other browsers aswell if you decide to take it to production website.
This approach does accept an unknown amount of items and text-widths, without any knowledge of their actual widths, and distribute them evenly across their container (in this case, #menu ul).
If you decide to be conservative, the approach suggested by Colin Brogan is the most acceptable given that you keep your texts on the same approximately length. If not, wider spaces will start to show.
Yes, you can do it, as long as the widths of the elements to be distributed are known in advance. But it's a bit messy.
The trick is, you want a spacing between each element of ‘(Wp-sum(Wc))/(Nc-1)’, that is width of the parent element minus the total width of all the child elements, divided equally between the number of gaps between the elements.
Because CSS doesn't have the ability to do expressions, we have to hack it a bit. First we add a margin to the parent element of the size ‘sum(Wc)’, the total width of all child elements. So now the parent has width ‘(Wp-sum(Wc))’, and we can use a padding value in % relative to that width.
So for example, for four images of sizes 10px, 20px, 40px and 80px respectively, our ‘sum(Wc)’ is 150px. Set that as the parent margin, then the children can have one-third of that width as padding between them.
<style type="text/css">
#nava { width: 10px; height: 20px;}
#navb { width: 20px; height: 20px;}
#navc { width: 40px; height: 20px;}
#navd { width: 80px; height: 20px;}
#nav { margin-right: 150px; white-space: nowrap; }
#nava, #navb, #navc { padding-right: 33.3%; }
</style>
<div id="nav"
><img id="nava" src="nava.png" alt="a"
><img id="navb" src="navb.png" alt="b"
><img id="navc" src="navc.png" alt="c"
><img id="navd" src="navd.png" alt="d"
></div>
The funny tag indentation is to avoid there being any whitespace between images. ‘nowrap’ is necessary because with the parent width set narrower than the page width, it wouldn't otherwise be possible to fit all the elements on the row. Finally, in IE you may need to add a wrapper div around the lot with ‘width: 100%; overflow: hidden’ to prevent unwanted scrollbars if you're spanning the whole page. And certainly you'll want to be in Standards Mode.
This can work with textual elements too, if you make them inline blocks so you can add padding, and you size them explicitly in ems. It won't work if the sizes of the child elements are not known in advance (eg. they contain dynamic content), as you won't know the ‘sum(Wc)’ value to use.
To be honest I would probably just use a table. The table layout algorithm copes very smoothly with calculating how to distribute spare table width. (Use ‘table-layout: fixed’ for best results with known-width cells, or ‘auto’ to respond to dynamic contents.) This way you also don't have to worry about pixel rounding errors.
If you were using text-based sizes (em, ex) it'd be a lot easier. You can then deal in letters rather than pixels.
Example: The whole thing is 30 capital letter Ms wide. You can then use the width of each nav element (based on its textual content) and do your math statically from there.
Here is the code in jQuery format for anyone who finds it useful
function justifyClients() {
var menuItems = $("#clients-wrapper ul li").get();
var menuWidth = $("#clients-wrapper ul").width();
var totalWidth = 0;
$("#clients-wrapper ul li").each(function(i,e)
{
totalWidth += $(e).width();
});
var margin = (menuWidth - 4 - totalWidth) / ($("#clients-wrapper ul li").length - 1);
margin = parseInt(margin);
$("#clients-wrapper ul li").each(function(i,e) {
if(i < $("#clients-wrapper ul li").length - 1)
{
alert(i + " " + $("#clients-wrapper ul li").length);
$(e).css('margin-right', margin);
}
});
}
$(document).ready(function() {
justifyClients();
});
The top answer didn't work for me, and GarciaWebDev's answer won't do it for me yet because I need to support a few other browsers, including IE8.
This method worked for me. The idea is to make a containing element text-align: justify and to make the elements to distribute display: inline-block.
HTML:
<div id="menu">
<ul>
<li>Home</li>
<li>News</li>
<li>Theme</li>
<li>Activities</li>
<li>Contact</li>
<li class="filler"></li>
</ul>
</div>
CSS:
#menu {
text-align: justify;
}
ul {
margin: 0;
padding: 0;
}
#menu li {
display: inline-block;
}
.filler {
width: 100%;
height: 0;
}
AFAIK there is no way to achieve this just with CSS.
Anybody correct me if this is wrong, pls.
If you use the Yahoo! User Interface Library (YUI) grids.css, it might work.
Demo - http://codepen.io/vsync/pen/tFwxu
all you need if to make the list itself text-align:justify and then add some pseudo item top the end of it and make it fill all the width, to trick the list into justifying all it's items across it's total width.
Trevor Dixon's improved variant (without extra <li>)
HTML
<ul>
<li>Home</li>
<li>News</li>
<li>Theme</li>
<li>Activities</li>
<li>Contact</li>
</ul>
CSS
ul {
margin: 0;
padding: 0;
}
ul li {
display: inline-block;
text-align: justify;
}
ul:after{
display: inline-block;
content: '';
width: 100%;
height: 0;
}
Thanks to the CSS3 Flexbox module, this is possible with two lines of CSS.
Check the Browser compatibility table for Flexbox
HTML
<div id="menu">
<ul>
<li>Home
</li>
<li>News
</li>
<li>Theme
</li>
<li>Activities
</li>
<li>Contact
</li>
</ul>
</div>
CSS
ul {
display: flex;
}
li {
flex: 1; /* Short hand for flex-grow: 1 and flex-shrink: 1 */
}
Output:
ul, li {
margin: 0;
padding: 0;
}
ul {
display: flex;
list-style: none;
}
li {
flex: 1;
text-align: center;
}
<div id="menu">
<ul>
<li>Home
</li>
<li>News
</li>
<li>Theme
</li>
<li>Activities
</li>
<li>Contact
</li>
</ul>
</div>

Categories