This question already has an answer here:
How to reorder divs using flex box?
(1 answer)
Closed 2 years ago.
I have got a Wordpress loop of posts. This outputs some kind of post-list. To make it easy, we can consider it a ordered-list like that:
<ol>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ol>
Each list item got a unique, different height. When a certain device width is given, I want them to display side by side, without a "row like behavior". So each column should place the next post right below it, like illustrated below (no unnecessary white space below the shorter items):
Using float, flex-box and css-grid or display: inline-block did not work for me.
Although I would love to avoid two loops with the same content inside my DOM, as its a bad behavior for screen-readers etc.
Is there a solution I do not see without a lot of javascript? The internet is full of float: left; examples searching for "two columns", "flexible columns" and I did not find anything helpful.
You can use display: flex and flex-direction: column;. By adding a height (or max-height) to the parent container, you make the elements automatically go to next column. Then you can change order attribute of some element to push them into the second row.
This solution is not very generic as it will depend on the content, but it may give an idea on how you can do it.
$('li').each(function() {
$(this).css('height',Math.floor((Math.random() * 50) + 30)+"px");
})
ol {
list-style: none;
display: flex;
flex-wrap: wrap;
flex-direction: column;
max-height: 100vh;
margin:0;
padding:0;
}
li {
box-sizing: border-box;
text-align:center;
padding: 10px;
background: red;
max-width: 50%;
margin: 5px;
}
li:nth-child(2n) {
background:green;
order:1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ol>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ol>
Important notice (added by #Marian Rick):
This approach does only work if the left column is bigger than the right one
You need to set a fixed height, which does not allow dynamic content
Both of these problems can be solved using a javascript snippet to keep the solution dynamic.
I have another fancy answer. It uses flex-direction:coulmn and the page-break-before to force every second elemend in the second column. This way you have no restriction to the height of the full list.
Please check the jsfiddle in a separate tab to check how I used the breakpoint to toggle from normal listing to two coulmns.
Also check if it runs in all targeted browser: https://caniuse.com/#search=page-break-before
section {
display: flex;
flex-wrap: wrap;
}
article {
box-sizing: border-box;
border:1px solid grey;
width: 100%;
height: 50px;
}
#media (min-width: 500px) {
section {
flex-direction: column;
}
article {
width: 50%;
}
article:nth-child(even) {
order: 2;
}
article:nth-child(2) {
/* this breaks into the second column after the 2nd child
(which is not the first element of the second half of elements) */
page-break-before: always;
}
}
/* just for demo */
article:first-child {
height: 66px;
background-color: #e0e0fe;
}
article:nth-child(4) {
height: 80px;
background-color: #aee0e0;
}
article:nth-child(6) {
height: 130px;
background-color: yellow;
}
<section>
<article>1</article>
<article>2</article>
<article>3</article>
<article>4</article>
<article>5</article>
<article>6</article>
<article>7</article>
</section>
Based on the great idea of #TemaniAfif, I have written a small, barely tested jQuery snippet, that achieves the following:
Each item will be placed as close as possible to the top, regarding to its position inside the container
While resizing the browser, each item updates its position
Its very few and fast javascript, while CSS does most of the work
The whole concept is still based on the idea of pushing items either to the left or right side, using the order: x attribute.
There is a CODEPEN DEMO to play around with.
Notice: Browser support is equal to the browser support of flex-box.
"use strict";
// DEMO STYLE - Should be removed
// calculate random heights for each item
$("li").each(function() {
$(this).css("height", Math.floor(Math.random() * 300 + 2) + "px");
});
///////////////////////
// Calculate columns
//
// 1. loop through each item.
// 2. first get the height of item
// 3. than check which column is shorter
// 4. if left column is shorter or equal, keep item on the left side
// 5. if right column is shorter, push this item to the right side
// 6. check which side will be higher
// 7. if left column is higher, assign height of column to parent container
// 8. if right column is higher, create a margin-bottom equal of the column offset and assign it to the left column
// calculation is finished. test it.
// finally add the height of the bigger column to the div
// if its the left column, assign the height of the right
var container = $("ol");
var items = container.find("li");
var breakPoint = 768; // if equal or bigger, the calculation will be fired
var calcPositions = function calcPositions() {
// quit function if its a mobile device
if ($(window).width() < breakPoint) return;
// reset margin of left column item
container.find("li.push-left").last().css("margin-bottom", "15px");
var leftColumnHeight = 0;
var rightColumnHeight = 0;
// 1. loop through each item
items.each(function(i, e) {
// 2. get height of item
var height = $(this).outerHeight(true);
// 3. check which column is shorter
if (leftColumnHeight <= rightColumnHeight) {
// 4. if left column is shorter or equal, keep item on the left side
leftColumnHeight += height;
$(this).removeClass("push-right").addClass("push-left");
return; // skip rest and continue with next item
}
// 5. if right column is shorter, push this item to the right side
// using .push-right { order: 5 } inside css
rightColumnHeight += height;
$(this).removeClass("push-left").addClass("push-right");
});
// 6. check which side will be higher
if (leftColumnHeight >= rightColumnHeight) {
// 7. if left column is higher, assign height of column to parent container
container.height(leftColumnHeight);
return; // end of function
}
// 8. if right column is higher, create a margin-bottom equal of the column offset and assign it to the left column
// otherwhise the second object can be displayed at the bottom of the left column
// get offset of columns
var columnOffset = rightColumnHeight - leftColumnHeight;
// assign offset to last element of left sidebar
container.find("li.push-left").last().css("margin-bottom", columnOffset + "px");
// assign height to container
container.height(rightColumnHeight);
};
// calculate initially
calcPositions();
// calculate on resize
$(window).resize(function() {
calcPositions();
});
/* functional classes needed for this grid */
/* keep this breakpoint in sync with "breakPoint" inside the javascript */
#media (min-width: 768px) {
ol {
display: flex;
flex-direction: column;
flex-wrap: wrap;
}
li {
max-width: 50%;
}
li.push-right {
order: 1;
margin-right: 0;
}
}
/* demo styles that can be removed */
* {
box-sizing: border-box;
}
ol {
padding: 0;
max-width: 800px;
width: 90%;
margin: 0 auto;
}
li {
background: red;
margin-bottom: 15px;
}
#media (min-width: 768px) {
li {
max-width: 49%;
margin-right: 2%;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Breakpoint is set to >=768px for two columns:</p>
<ol>
<li>Lorem.</li>
<li>Asperiores!</li>
<li>Illum!</li>
<li>Perspiciatis!</li>
<li>Eius.</li>
<li>Est.</li>
<li>Quisquam.</li>
<li>Eaque!</li>
<li>Vero?</li>
<li>Iste?</li>
<li>Provident?</li>
<li>Ipsum.</li>
</ol>
You can do this with inline-block if you want you avoid flex for some reason. Just use media queries when you to have all items in one column.
div {
width: 95%;
margin: auto;
}
li {
background: red;
width: 49%;
min-height: 300px;
display: inline-block;
text-align: center;
font-size: 50px;
margin-top: 10px;
}
#media screen and (max-width: 600px) {
li {
width: 98%;
}
}
.one {
background: blue;
}
.three {
background: green;
}
<div>
<ol>
<li class='one'>1</li>
<li>2</li>
<li class='three'>3</li>
<li>4</li>
<li>5</li>
</ol>
</div>
I have a horizontally sliding line of elements within a fixed width, so you have to scroll left an right to see all the elements. See JS Fiddle and text-only example below.
Hi | Hello | How do you do | Fine thanks | Good weather this time of year
My question is: How can I center any given element horizonally? Eg. Put the horizontal center of element number 3 in the horizontal center of the surrounding div.
If the element can't be centered because it is at the beginning of the line for example, that's OK, then the H-position should simply be 0. It should only be centered as much as it can be.
So something like this? You can use a combination of the following native properties scrollLeft, offsetLeft, and offsetWidth.
var items = document.querySelectorAll('.wrapper ul li');
function centerItems(which) {
var wrapper = items[which].offsetParent;
wrapper.scrollLeft =
(items[which].offsetLeft + items[which].offsetWidth / 2)
- wrapper.offsetWidth / 2;
}
html {
background: #eee;
}
body {
width: 320px;
background: white;
font-family: sans-serif;
}
.wrapper {
overflow-x: scroll;
position: relative;
}
ul {
white-space: nowrap;
}
li {
display: inline-block;
color: #898;
background: #efe;
padding: 8px;
}
<p>Hello!</p>
<button onclick="centerItems(0)">1</button>
<button onclick="centerItems(1)">2</button>
<button onclick="centerItems(2)">3</button>
<button onclick="centerItems(3)">4</button>
<button onclick="centerItems(4)">5</button>
<div class="wrapper">
<ul>
<li>Short</li>
<li>Very long line here</li>
<li>Medium line</li>
<li>Another one Another one Another one</li>
<li>Yet another</li>
</ul>
</div>
<p>Goodbye!</p>
I am creating a navigation/menu bar and I want it to move horizontally as the cursor moves over it. Currently that is working but I do not want to set the overflow property to hidden because I have drop down tabs that should appear when the cursor is over a menu option. If I were to use overflow then it will cut off at a point but the drop down is also cut off. Inside scroll contains the menu options.
var sum = 0;
$("#scroll li").each(function () {
sum += $(this).width() + parseInt($(this).css('paddingLeft')) + parseInt($(this).css('paddingRight'))
});
$("#scroll").css('width', sum);
$("#holder").mousemove(function (e) {
x = -(((e.pageX - $('#scroll').position().left) / $("#holder").width()) * ($("#scroll").width() + parseInt($("#scroll").css('paddingLeft')) + parseInt($("#scroll").css('paddingRight')) - $("#holder").width()));
$("#scroll").css({
'marginLeft': x + 'px'
});
});
#scroll{
height: 30px;
line-height: 30px;
margin-left: 0;
padding: 0 10px;
}
#scroll li {
float: left;
padding: 0 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="holder" style="width: 800px;line-height: 30px; /* overflow: hidden; */margin-left: 10px;float: left;">
<div id="scroll" class="nav-collapse"> </div>
</div>
Please help provide an alternate solution to using overflow.
Without the entire sample of code or a JSFiddle reference I can't really help you but you can use overflow-x : hidden I guess.
I try to create a menu that streches its items to the window width. Could someone please tell me what I'm doing wrong? I know that this question has been asked before but I just don't know what's wrong with my code.
This is what I"m trying to achieve. The red is the window
http://jsfiddle.net/JdGeQ/5/
Javascript
$(function(){
$(window).resize(function (e) {
setTimeout(function () {
resizeButtons();
}, 200);
});
resizeButtons();
});
function resizeButtons() {
var count = $("#menu").length;
var itemwidth = $(window).width / count;
$(".item").css("background", "blue");
$(".item").css("width", itemwidth);
}
css
.elementtop {
position: fixed;
top: 0;
}
.elementfooter {
position: fixed;
bottom: 0;
}
.elementright {
right: 0;
}
ul {
min-width:100%;
padding:0;
margin:0;
float:left;
list-style-type:none;
background: #000000;
}
li {
display:inline;
}
a {
overflow: hidden;
text-decoration:none;
color:white;
background-color:purple;
padding:0.2em 0.6em;
border-right:1px solid white;
}
html
<div>
<ul id="menu">
<li> <a href="#" class="item">
<span>Text text</span>
</a>
</li>
<li> <a href="#" class="item">
<span>Text2</span>
</a>
</li>
<li> <a href="#" class="item">
<span>Text3</span>
</a>
</li>
</ul>
</div>
Thanks in advance.
You have a couple errors in your jQuery code. You need to use width() instead of width, as it is a function call. Also, you are not selecting menu items when you assign count, you are only selecting the #menu.
function resizeButtons() {
var count = $("#menu .item").length;
var itemwidth = $(window).width();
itemwidth = itemwidth / count;
$(".item").css(
"width", itemwidth
);
}
You also need to set display:inline-block or display:block on your anchors, so that you can affect the width.
a { display:inline-block; }
Updated Fiddle
Note: You will also need to account for the padding, etc. on your menu items to get the proper result.
This can be done with pure CSS:
http://cssdeck.com/labs/hgmvgwqc
ul {
min-width:100%;
padding:0;
margin:0;
display: table;
list-style-type:none;
background: #000000;
}
li {
display: table-cell;
width: 33%;
}
a {
overflow: hidden;
text-decoration:none;
color:white;
background-color:purple;
padding:0.2em 0.6em;
border-right:1px solid white;
display: block;
}
This method requires knowing how many list items there are.
With $("#menu").length you're getting the number of occurrences of the #menu element -- 1. You should use the following to get the number of menu items
var count = $("#menu li").length;
I have two divs inside of a container. One on the left, one on the right, side by side. How am I able to make each one be of equal height, even though they have different content.
For example, the right div has a lot of content, and is double the height of the left div, how do I make the left div stretch to the same height of the right div?
Is there some JavaScript (jQuery) code to accomplish this?
You could use jQuery, but there are better ways to do this.
This sort of question comes up a lot and there are generally 3 answers...
1. Use CSS
This is the 'best' way to do it, as it is the most semantically pure approach (without resorting to JS, which has its own problems). The best way is to use the display: table-cell and related values. You could also try using the faux background technique (which you can do with CSS3 gradients).
2. Use Tables
This seems to work great, but at the expense of having an unsemantic layout. You'll also cause a stir with purists. I have all but avoided using tables, and you should too.
3. Use jQuery / JavaScript
This benefits in having the most semantic markup, except with JS disabled, you will not get the effect you desire.
Here's a way to do it with pure CSS, however, as you'll notice in the example (which works in IE 7 and Firefox), borders can be difficult - but they aren't impossible, so it all depends what you want to do. This example assumes a rather common CSS structure of body > wrapper > content container > column 1 and column 2.
The key is the bottom margin and its canceling padding.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Equal Height Columns</title>
<style type="text/css">
<!--
* { padding: 0; margin: 0; }
#wrapper { margin: 10px auto; width: 600px; }
#wrapper #main_container { width: 590px; padding: 10px 0px 10px 10px; background: #CCC; overflow: hidden; border-bottom: 10px solid #CCC; }
#wrapper #main_container div { float: left; width: 263px; background: #999; padding: 10px; margin-right: 10px; border: 1px solid #000; margin-bottom: -1000px; padding-bottom: 1000px; }
#wrapper #main_container #right_column { background: #FFF; }
-->
</style>
</head>
<body>
<div id="wrapper">
<div id="main_container">
<div id="left_column">
<p>I have two divs inside of a container. One on the left, one on the right, side by side. How am I able to make each one be of equal height, even though they have different content.</p>
</div><!-- LEFT COLUMN -->
<div id="right_column">
<p>I have two divs inside of a container. One on the left, one on the right, side by side. How am I able to make each one be of equal height, even though they have different content.</p>
<p> </p>
<p>For example, the right div has a lot of content, and is double the height of the left div, how do I make the left div stretch to the same height of the right div?</p>
<p> </p>
<p>Is there some JavaScript (jQuery) code to accomplish this?</p>
</div><!-- RIGHT COLUMN -->
</div><!-- MAIN CONTAINER -->
</div><!-- WRAPPER -->
</body>
</html>
This is what it looks like:
you can get it working with js:
<script>
$(document).ready(function() {
var height = Math.max($("#left").height(), $("#right").height());
$("#left").height(height);
$("#right").height(height);
});
</script>
I've seen many attempts to do this, though none met my OCD needs. You might need to dedicate a second to get your head around this, though it is better than using JavaScript.
Known downsides:
Does not support multiple element rows in case of a container with dynamic width.
Does not work in IE6.
The base:
red is (auxiliary) container that you would use to set margin to the content.
green is position: relative; overflow: hidden and (optionally, if you want columns to be centered) text-align: center; font-size: 0; line-height: 0;
blue display: block; float: left; or (optionally, if you want columns to be centered) display: inline-block; vertical-align: top;
So far nothing out of ordinary. Whatever content that blue element has, you need to add an absolutely positioned element (yellow; note that the z-index of this element must be lower than the actual content of the blue box) with this element and set top: 0; bottom: 0; (don't set left or right position).
All your elements now have equal height. For most of the layouts, this is already sufficient. My scenario required to have dynamic content followed by a static content, where static content must be on the same line.
To achieve this, you need to add padding-bottom (dark green) eq to the fixed height content to the blue elements.
Then within the yellow elements create another absolutely positioned (left: 0; bottom: 0;) element (dark blue).
Supposedly, if these boxes (yellow) had to be active hyperlinks and you had any style that you wanted to apply to the original blue boxes, you'd use adjacent sibling selector:
yellow:hover + blue {}
Here is a the code and demo:
HTML:
<div id="products">
<ul>
<li class="product a">
<a href="">
<p class="name">Ordinary product description.</p>
<div class="icon-product"></div>
</a>
<p class="name">Ordinary product description.</p>
</li>
<li class="product b">
<a href="">
<p class="name">That lenghty product description or whatever else that does not allow you have fixed height for these elements.</p>
<div class="icon-product"></div>
</a>
<p class="name">That lenghty product description or whatever else that does not allow you have fixed height for these elements.</p>
</li>
<li class="product c">
<a href="">
<p class="name">Another ordinary product description.</p>
<div class="icon-product"></div>
</a>
<p class="name">Another ordinary product description.</p>
</li>
</ul>
</div>
SCSS/LESS:
#products {
ul { position: relative; overflow: hidden; text-align: center; font-size: 0; line-height: 0; padding: 0; margin: 0;
li { display: inline-block; vertical-align: top; width: 130px; padding: 0 0 130px 0; margin: 0; }
}
li {
a { display: block; position: absolute; width: 130px; background: rgba(255,0,0,.5); z-index: 3; top: 0; bottom: 0;
.icon-product { background: #ccc; width: 90px; height: 90px; position: absolute; left: 20px; bottom: 20px; }
.name { opacity: 1; }
}
.name { position: relative; margin: 20px 10px 0; font-size: 14px; line-height: 18px; opacity: 0; }
a:hover {
background: #ddd; text-decoration: none;
.icon-product { background: #333; }
}
}
}
Note, that the demo is using a workaround that involves data-duplication to fix z-index. Alternatively, you could use pointer-events: none and whatever solution for IE.
here is very simple solution with a short css display:table
<div id="main" class="_dt-no-rows">
<div id="aside" contenteditable="true">
Aside
<br>
Here's the aside content
</div>
<div id="content" contenteditable="true">
Content
<br>
geht's pellentesque wurscht elementum semper tellus s'guelt Pfourtz !. gal hopla
<br>
TIP : Just clic on this block to add/remove some text
</div>
</div>
here is css
#main {
display: table;
width: 100%;
}
#aside, #content {
display: table-cell;
padding: 5px;
}
#aside {
background: none repeat scroll 0 0 #333333;
width: 250px;
}
#content {
background: none repeat scroll 0 0 #E69B00;
}
its look like this
Well, I don't do a ton of jQuery, but in the CSS/Javascript world I would just use the object model and write a statement as follows:
if(leftDiv.style.height > rightDive.style.height)
rightDiv.style.height = leftDiv.style.height;
else
leftDiv.style.height = rightDiv.style.height)
There's also a jQuery plugin called equalHeights that I've used with some success.
I'm not sure if the one I'm using is the one from the filament group mentioned above, or if it's this one that was the first google result... Either way a jquery plugin is probably the easiest, most flexible way to go.
Use this in jquery document ready function. Considering there are two divs having ids "left" and "right."
var heightR = $("#right").height();
var heightL = $("#left").height();
if(heightL > heightR){
$("#right").css({ height: heightL});
} else {
$("#left").css({ height: heightR});
}
Although many disagree with using javascript for this type of thing, here is a method that I used to acheive this using javascript alone:
var rightHeight = document.getElementById('right').clientHeight;
var leftHeight = document.getElementById('left').clientHeight;
if (leftHeight > rightHeight) {
document.getElementById('right').style.height=leftHeight+'px';
} else {
document.getElementById('left').style.height=rightHeight+'px';
}
With "left" and "right" being the id's of the two div tags.
This is what I use in plain javascript:
Seems long, but is very uncomplicated!
function equalizeHeights(elements){
//elements as array of elements (obtain like this: [document.getElementById("domElementId"),document.getElementById("anotherDomElementId")]
var heights = [];
for (var i=0;i<elements.length;i++){
heights.push(getElementHeight(elements[i],true));
}
var maxHeight = heights[biggestElementIndex(heights)];
for (var i=0;i<elements.length;i++){
setElementHeight(elements[i],maxHeight,true);
}
}
function getElementHeight(element, isTotalHeight){
// isTotalHeight triggers offsetHeight
//The offsetHeight property is similar to the clientHeight property, but it returns the height including the padding, scrollBar and the border.
//http://stackoverflow.com/questions/15615552/get-div-height-with-plain-javascript
{
isTotalHeight = typeof isTotalHeight !== 'undefined' ? isTotalHeight : true;
}
if (isTotalHeight){
return element.offsetHeight;
}else{
return element.clientHeight;
}
}
function setElementHeight(element,pixelHeight, setAsMinimumHeight){
//setAsMinimumHeight: is set, we define the minimum height, so it can still become higher if things change...
{
setAsMinimumHeight = typeof setAsMinimumHeight !== 'undefined' ? setAsMinimumHeight : false;
}
var heightStr = "" + pixelHeight + "px";
if (setAsMinimumHeight){
element.style.minHeight = heightStr; // pixels
}else{
element.style.height = heightStr; // pixels
}
}
function biggestElementIndex(arr){
//http://stackoverflow.com/questions/11301438/return-index-of-greatest-value-in-an-array
var max = arr[0];
var maxIndex = 0;
for (var i = 1; i < arr.length; i++) {
if (arr[i] > max) {
maxIndex = i;
max = arr[i];
}
}
return maxIndex;
}
I agree with initial answer but the JS solution with equal_heights() method does not work in some situations, imagine you have products next to each other. If you were to apply it only to the parent container yes they will be same height but the product name sections might differ if one does not fit to two line, this is where i would suggest using below
https://jsfiddle.net/0hdtLfy5/3/
function make_children_same_height(element_parent, child_elements) {
for (i = 0; i < child_elements.length; i++) {
var tallest = 0;
var an_element = child_elements[i];
$(element_parent).children(an_element).each(function() {
// using outer height since that includes the border and padding
if(tallest < $(this).outerHeight() ){
tallest = $(this).outerHeight();
}
});
tallest = tallest+1; // some weird shit going on with half a pixel or something in FF and IE9, no time to figure out now, sowwy, hence adding 1 px
$(element_parent).children(an_element).each(function() {
$(this).css('min-height',tallest+'px');
});
}
}