I have three divs. The first one (A) has a width and a heigth of 100%. The next (B) has a width of 80% and a height which is variable and solely determined by its content. I want to position B on A's bottom line, such that B's middle point is aligned with A's. I managed to accomplish this using transform: translateY(-50%);.
However, the following div (C, also variable height) does not follow (B) and leaves a huge gap, which I want to get rid of.
Do you have any ideas how to close the gap? The difficulty is that it is impossible to know B's height in advance.
Thanks!
Herberth
You can use this HTML file:
#a {
background: #6671ff;
height: 100px;
}
#b {
background: #f171ff;
width: 80%;
height: 80px;
transform: translateY(-50%);
}
#c {
background: #663699;
height: 200px;
}
<div id="a">A</div>
<div id="b">B</div>
<div id="c">C</div>
You can use the following jQuery code to get the height of #b and then divide it by -2 (= half, but negative value) and apply that as a negative margin-top to #b:
var height_of_b = $('#b').outerHeight();
var offset = height_of_b / -2;
$('#b').css('margin-top', offset);
#a {
background: #6671ff;
height: 100px;
}
#b {
background: #f171ff;
width: 80%;
height: 80px;
position: relative;
}
#c {
background: #663699;
height: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="a">A</div>
<div id="b">B</div>
<div id="c">C</div>
var b = document.getElementById("b");
var c = document.getElementById("c");
c.style.top = (-(b.offsetHeight / 2)) + "px";
#a {
background: #6671ff;
height: 200px;
position: relative;
}
#b {
background: #f171ff;
width: 80%;
height: 180px;
transform: translateY(-50%);
border: 3px solid aqua;
padding: 10px;
}
#c {
background: #663699;
height: 200px;
position: relative;
}
<div id="a">A</div>
<div id="b">B</div>
<div id="c">C</div>
If you're alright using some javascript, this gets the job done with 3 lines of javascript and some additional CSS. It also accounts for any padding or borders you place on Element #b, so I thought it would be useful in case you were going to add some additional styling onto that middle element. It works with any height that the middle element is set to, and also does not depend on the height of either the top or bottom elements.
My method uses a combination of CSS' position: relative and javascript's offsetHeight to work. Basically, it gets the height of element B through javascript (and this will get whatever the height is, including padding, border, and any size changes caused by the element's inner content) and then divides it by two in order to get half of the height of element B. Then, it moves the top of element C back by that calculated amount of pixels.
It also doesn't need any Jquery.
Try out the code snippet, it works. Hope this helps!
if you can modify the markup, depending on your use-case you can do something like this:
#a {
border:1px dashed #6671ff;
height: 100px;
position: relative;
}
#b {
border:1px dashed #f171ff;
width: 80%;
height: 80px;
margin: 0 auto;
}
#c {
border:1px dashed orange;
height: 200px;
}
#bc {
position: absolute;
top: 50%;
left: 0;
width: 100%;
z-index: 1;
}
body {
padding: 20px;
}
<div id="a">
A
<div id="bc">
<div id="b">B</div>
<div id="c">C</div>
</div>
</div>
You can use Javascript to add a listener to the second box's size changes. Every time it changes, calculate half of it's new height and set the transform property of the third box accordingly.
const secondBox = document.querySelector('.box2');
const thirdBox = document.querySelector('.box3');
function outputsize() {
const transformHeight = secondBox.offsetHeight / 2;
thirdBox.style.transform = `translateY(-${transformHeight}px)`;
}
outputsize()
new ResizeObserver(outputsize).observe(secondBox)
* {
margin: 0;
}
.box {
width: 200px;
}
.box1 {
height: 200px;
background: blue;
}
.box2 {
transform: translateY(-50%);
background: pink;
opacity: .5;
}
.box3 {
background: purple;
height: 50px;
}
<div class="box box1"></div>
<div class="box box2">
<p>dummydata</p>
<p>dummydata</p>
<p>dummydata</p>
<p>dummydata</p>
<p>dummydata</p>
<p>dummydata</p>
<p>dummydata</p>
</div>
<div class="box box3"></div>
Please note: the ResizeObserver api isn't supported in all browsers yet. You can get more info on the matter here.
Here is an HTML/CSS only version. I modified it with keeping the heights dynamic using by setting position: absolute; and transform: translateY(50%); on div B inside the position: relative; div A.
#a {
background: #6671ff;
height: 100px;
margin-bottom: 50px;
position: relative;
}
#b {
background: #f171ff;
width: 80%;
height: 80px;
position: absolute;
transform: translateY(50%);
}
#c {
background: #663699;
height: 200px;
}
<div id="wrapper">
<div id="a">
A
<div id="b">B</div>
</div>
<div id="c">C</div>
</div>
Related
I am not familiar with CSS. When setting style for various elements, is there a clean way to set the dimensions of the elements that depend on each others? In the example below, suppose that I need to change the height of either container2 or container3 and keep their total height to be the same as that of container 1, I have to go though the css file and modify their heights manually one by one keeping in mind this summation constraint. For eg, if I have to change the height of container2 to be 100px, I have to change that of container3 to 400px, so as to maintain a sum of 500px. This situation get messier when there are more elements whose dimensions depend on each other. Is there a way to make such changes dynamic?
For eg, can we do something like: set two variables to be height2 and heigh3 and set a constraint height2 + height3 = 500px. So that if either height2 or height3 is changed, some simple calculation can be executed to maintain the 500px constraint?
#container1 {
height: 30%;
width: 300px;
}
#container2 {
height: 70%;
width: 300px;
background-color: blue;
}
#container3 {
height: 300px;
width: 300px;
background-color: yellow;
}
<div id="container1">
<div id="container2">
</div>
<div id="container3">
</div>
</div>
This is just an example of using CSS vars. You can find more info here https://css-tricks.com/difference-between-types-of-css-variables/
:root {
--main-width: 300px;
--height-one: 200px;
--height-two: 300px;
}
#container1 {
background: red;
/* 60px just to show it works, because you don't need to set height anyway here */
height: calc(var(--height-one) + var(--height-two) + 60px);
width: var(--main-width);
}
#container2 {
height: var(--height-one);
width: var(--main-width);
background-color: blue;
}
#container3 {
height: var(--height-two);
width: var(--main-width);
background-color: yellow;
}
<div id="container1">
<div id="container2"></div>
<div id="container3"></div>
</div>
But what you should really do is use percentages, as Paulie_D has suggested in comments.
#container1 {
background: red;
height: 500px;
width: 300px;
}
#container2 {
height: 40%;
background-color: blue;
}
#container3 {
height: 60%;
background-color: yellow;
}
<div id="container1">
<div id="container2">
</div>
<div id="container3">
</div>
</div>
In your particular example, if main container need to adjust the heights of children, you just not need to set its height, it will have its content height by default, and once you set main container height, child div will have 100% width by default.
In general, there is a way to use variables in css using --name notation. and simplee calculations using calc function. like this:
:root { /*define variables*/
--w: 300px;
--h1: 200px;
--h2: 300px;
}
#container1 {
height: calc(var(--h1) + var(--h2)); /* css calculation */
width: var(--w); /* simple use of variable*/
}
#container2 {
height: var(--h1);
width: var(--w);
background-color: blue;
}
#container3 {
height: var(--h2);
width: var(--w);
background-color: yellow;
}
<div id="container1">
<div id="container2">
</div>
<div id="container3">
</div>
</div>
I have a parent container with a fixed size, and absolutely positioned child divs inside it. The position of the child divs have dynamic text content and a max-width and can move around freely, and extend outside of the parent, which is set to overflow: hidden. See the snippet at the bottom for illustration.
This works fine except for one small problem: If a child partially sticks outside the parent container to the right, the text inside of it will wrap, trying to stay inside of the parent container. I do explicitly not want this - if a child moves out of the parent, it should not change its wrapping or size because of it, it should just move out of view. The parent should simply act as a "window" of sorts through which the children are viewed.
I've tried different combinations of applying white-space: nowrap to the parent and/or children, none of which have worked for me so far. Is this just not possible?
This snippet demonstrates the problem:
/* Relevant parent and child styles */
.parent {
position: relative;
width: 500px;
height: 400px;
overflow: hidden;
}
.child {
position: absolute;
max-width: 250px;
}
/* Individual positioning of child elements */
#fine {
top: 30px;
left: 100px;
}
#wrapped {
top: 90px;
left: 400px;
}
#unwrapped {
top: 330px;
left: 170px;
white-space: nowrap;
}
/* The rest is only styles to make the example easier on the eye */
body {
background-color: hsl(0, 0%, 90%);
margin: 0;
padding: 24px;
}
.parent {
background-color: white;
border: 2px solid grey;
}
.child {
padding: 12px;
background: yellowgreen;
}
<div class="parent">
<div id="fine" class="child">
Text should be wrapped normally with the max-width of the child, like this
</div>
<div id="wrapped" class="child">
This text will be wrapped much earlier though because it is running out of the parent container
</div>
<div id="unwrapped" class="child">
"white-space: nowrap" only prevents *all* wrapping
</div>
</div>
Don't use left to position the element but consider translate instead. The left property will restrict the width of your element.
/* Relevant parent and child styles */
.parent {
position: relative;
width: 500px;
height: 400px;
}
.child {
position: absolute;
max-width: 250px;
}
/* Individual positioning of child elements */
#fine {
top: 30px;
transform: translateX(100px);
}
#wrapped {
top: 90px;
transform: translateX(400px);
}
#unwrapped {
top: 200px;
left: 170px;
}
#extra {
top: 300px;
left: 170px;
}
/* The rest is only styles to make the example easier on the eye */
body {
background-color: hsl(0, 0%, 90%);
margin: 0;
padding: 24px;
}
.parent {
background-color: white;
border: 2px solid grey;
}
.child {
padding: 12px;
background: yellowgreen;
}
<div class="parent">
<div id="fine" class="child">
Text should be wrapped normally with the max-width of the child, like this
</div>
<div id="wrapped" class="child">
This text will be wrapped much earlier though because it is running out of the parent container
</div>
<div id="unwrapped" class="child">
"white-space: nowrap" only prevents *all* wrapping
</div>
<div id="extra" class="child">
short text
</div>
</div>
Or consider a big negative margin (at least equal to max-width) to negate the use of left:
/* Relevant parent and child styles */
.parent {
position: relative;
width: 500px;
height: 400px;
}
.child {
position: absolute;
max-width: 250px;
margin-right:-250px;
}
/* Individual positioning of child elements */
#fine {
top: 30px;
left:100px;
}
#wrapped {
top: 90px;
left: 400px;
}
#unwrapped {
top: 200px;
left: 170px;
}
#extra {
top: 300px;
left: 170px;
}
/* The rest is only styles to make the example easier on the eye */
body {
background-color: hsl(0, 0%, 90%);
margin: 0;
padding: 24px;
}
.parent {
background-color: white;
border: 2px solid grey;
}
.child {
padding: 12px;
background: yellowgreen;
}
<div class="parent">
<div id="fine" class="child">
Text should be wrapped normally with the max-width of the child, like this
</div>
<div id="wrapped" class="child">
This text will be wrapped much earlier though because it is running out of the parent container
</div>
<div id="unwrapped" class="child">
"white-space: nowrap" only prevents *all* wrapping
</div>
<div id="extra" class="child">
short text
</div>
</div>
To understand how both tricks work you need to refer to the specification where you can find the formula to calculate the width of your element:
The constraint that determines the used values for these elements is:
'left' + 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' + 'right' = width of containing block
You can then read the different case where some values are specified and other auto
Why are child positions affected when you transform the parent?
I want the blue box stay in the bottom right position of the yellow box. But when I translate the red box, the blue box will move to his (red) parent.
In real life box-red represents my ui-view in Angular. The views are sliding in and out. I can't change the HTML hierarchy.
See my codepen
https://codepen.io/benbesuijen/pen/GPOQjM
HTML
<div class="box-yellow">
<div class="box-red">
<div class="box-blue"></div>
</div>
</div>
CSS
.box-yellow {
background-color: yellow;
position: relative;
height: 200px;
width: 200px;
}
.box-red {
background-color: red;
height: 100px;
width: 100px;
}
.box-blue {
background-color: blue;
bottom: 0;
height: 50px;
position: absolute;
right: 0;
width: 50px;
}
.box-move {
transform: translateX(100%);
}
Take a look at the spec: The Transform Rendering Model
Specifying a value other than ‘none’ for the ‘transform’ property
establishes a new local coordinate system at the element that it is
applied to.
What that means here is that the blue element will become relative to the element with the transform (the red parent) - not relative to the viewport (like regular static elements)
However, we can solve this case by applying the transform to the yellow-box, and have the the blue one's position: fixed.
Below is an example:
var button = document.querySelector('button'),
boxRed = document.querySelector('.box-red');
button.addEventListener('click', function() {
boxRed.classList.toggle('box-move');
});
.box-yellow {
background-color: yellow;
transform: translate(0, 0);
float: left;
position: relative;
height: 200px;
width: 200px;
}
.box-red {
background-color: red;
height: 100px;
width: 100px;
}
.box-blue {
background-color: blue;
position: fixed;
bottom: 0;
right: 0;
height: 50px;
width: 50px;
}
.box-move {
margin-left: 50%;
}
button {
margin-left: 20px;
}
<div class="box-yellow">
<div class="box-red">
<div class="box-blue"></div>
</div>
</div>
<button>Translate red box</button>
Hope this helps :)
I think your only way is to use margin-left and calculate the size of the box. Something like:
button.addEventListener('click', function() {
var boxRedWidth = boxRed.getBoundingClientRect().width;
boxRed.style.marginLeft = boxRedWidth +"px";
});
It's due to the translateX essentially making it a relative position object, meaning the .box-blue jumps into that as its relative parent.
With margin-left the .box-red remains as static meaning it doesn't become a relative parent to box-blue.
I have a DIV like below
<div class="parent">
<div class="fixedHt"></div>
<div class="fluidHt"></div>
</div>
I have written CSS like below
.fixedHt{
height:30px;
}
.fluidHt{
margin-top:30px;
}
I want to achieve the same with columns I can achieve with floats, how can I achieve this in rows?
jsFiddle Demo
HTML:
<div class="parent">
<div class="fixedHt small"></div>
<div class="fluidHt streched"></div>
</div>
CSS:
.streched {
position: absolute;
top: 30px;
bottom: 0;
left: 0;
right: 0;
}
For browsers who support CSS3, use calc function (no need for absolute positioning):
see this Fiddle
HTML:
<div class="parent">
<div class="fixedHt"></div>
<div class="fluidHt"></div>
</div>
CSS
.parent {
height: 300px;
width: 300px;
border: 3px solid #000;
}
.fixedHt
{
height: 30px;
background-color: red;
}
.fluidHt
{
height: calc(100% - 30px);
background-color: blue;
}
Tested on: IE10, IE10 using IE9 mode, FF, Chrome
Edit:
CSS3 is not supported in IE8, so instead you can use height: 270px; in the .fluidHt rule (that is only if the fixed height is not a problem for you) like this Fiddle [Works with all Broswers],
or you can apply a Script that fix the second div's height dynamically. like this Fiddle [Works with all Broswers]
I write here another answer, that don't use calc() and don't relay on the fixed height of the first div, so even if it's height is changing, the second will always span the rest of the container.
also: it has better browser support (IE8+ & all major browsers), and its Pure CSS.
Check this Working Fiddle
HTML: (same)
<div class="parent">
<div class="fixedHt"></div>
<div class="fluidHt"></div>
</div>
CSS:
.parent
{
height: 300px;
width: 300px;
border: 3px solid #000;
}
.parent:before
{
content: '';
height: 100%;
float: left;
}
.fixedHt
{
height: 30px; /*can be any height*/
background-color: red;
}
.fluidHt
{
background-color: blue;
}
.fluidHt:after
{
content: '';
clear: both;
display: block;
}
I was trying to see if I could get layout to work something like what graphviz does when it groups nodes. Essentially, I can position nodes in whatever location and then lay out a box around those with a dotted outline.
So I had some HTML like this:
<div class="graph">
<div class="group" id="group1">
<div class="node" id="node1">1</div>
<div class="node" id="node2">2</div>
</div>
<div class="group" id="group2">
<div class="node" id="node3">3</div>
<div class="node" id="node4">4</div>
</div>
</div>
I want the position of the nodes to be the main thing I'm controlling. I figured out that I could use absolute positioning for both, at the cost of having to do maths on the position of the nodes, because they become relative to the group. So I end up with CSS like this:
div.graph {
position: relative;
background: yellow;
padding: 0;
}
div.group {
position: absolute;
border: 1px solid red;
background: green;
width: 100%;
padding: 0;
}
div.node {
position: absolute;
background: blue;
height: 20pt;
padding: 0;
}
#group1 { left: 20pt; top: 20pt; height: 40pt; }
#group2 { left: 50pt; top: 60pt; height: 40pt; }
#node1 { left: /*20-20=*/0pt; top: /*20-20=*/0pt; }
#node2 { left: /*200-20=*/180pt; top: /*40-20=*/20pt; }
#node3 { left: /*50-50=*/0pt; top: /*60-60=*/0pt; }
#node4 { left: /*800-50=*/750pt; top: /*80-60=*/20pt; }
The result of this can be seen on the JSFiddle I was using to try and fix it.
Essentially, the bit I can't figure out is the width of the groups. I don't know how wide the nodes are going to be because the contents are variable. I tried using 100% as a compromise but it didn't go 100% of the width anyway, once a horizontal scrollbar is involved. Ideally there would be a way to get it to end exactly on the right hand side of the node (later I can work out the padding situation... at the moment it's set to 0 for everything.)
I didn't want to use JavaScript to figure out the width dynamically because I was worried about having to cope with the width changing.
If all else fails I might just have to use SVG but I have various issues over in SVG as well (text layout isn't as convenient as it is in HTML.)
You can't do this with absolute positioning. As per the spec,
In the absolute positioning model, a box is removed from the normal
flow entirely (it has no impact on later siblings) and assigned a
position with respect to a containing block.
Your best bet is using floats and margins: http://jsfiddle.net/WspTQ/
<div class="a">
<div class="c">
<div class="x"></div>
<div class="y"></div>
</div>
</div>
<div class="b">
<div class="c">
<div class="x"></div>
<div class="y"></div>
</div>
</div>
.a, .b {
margin-bottom:10px;
float:left;
clear:both;
background:#eee
}
.x, .y {
width:50px;
height:50px;
background:black
}
.x {
margin-bottom:-50px
}
.a .y {
margin-left:100px;
margin-top:20px
}
.b .y {
margin-top:75px;
margin-left:150px
}
I don't know if I understood you well. But try this:
div.group {
position: absolute;
border: 1px solid red;
background: green;
width: auto;
min-width: ;// if you need one;
max-width: ;// if you need one;
padding: 0;
}