Extend height to include absolutely positioned children - javascript

I'm building an html/javascript theme designer for a CMS. Elements are positioned absolutely and can be moved/resized via the mouse, and/or contain editable text whose height may be determined by the number of lines. However I'm running into the problem where a parent element's height does not expand to include its absolutely positioned children.
Minimal code (also on JSFiddle here):
<style>
div.layer { position: absolute }
div.layer1 { width: 400px; border: 1px solid #ccc }
div.layer2 { top: 15px; left: 100px; width: 100px; border: 1px solid blue }
</style>
<div class="layer layer1">container should expand to the bottom of the child.
<div class="layer layer2" contentEditable>Child with<br/>editable text.</div>
</div>
A CSS-only solution is ideal and I'm not concerned about older browsers. But I'm mostly looking for any way to prevent the need for javascript to run on every page using a created theme to set their height (since pages with the same theme may have different amounts of text).
There are already a few similar questions but their accepted answers (e.g. don't use absolute positioning) won't work in my case. Unless there is a way to have multiple layers of draggable/resizable elements without them being position: absolute.

I found a pure-css solution! In summary:
Set the child elements to position: relative instead of absolute.
Set their margin-right to be their negative width, to give them zero effective width, and make them float: left to keep them all on the same line. This makes all of them have an origin of 0, 0.
Then we can set their left and margin-top properties to position them absolutely within their parents. Note that margin-top is required instead of top because top won't push down the bottom of the parent element.
JSFiddle here or code below:
<style>
div.layer { position: relative; float: left; }
div.layer1 { width: 400px; border: 1px solid black }
div.layer2 { margin-top: 20px; left: 100px; width: 100px; margin-right: -100px; border: 1px solid blue }
div.layer3 { margin-top: 30px; left: 170px; width: 100px; margin-right: -100px; border: 1px solid red }
div.layer4 { margin-top: 30px; left: 20px; width: 60px; margin-right: -60px; border: 1px solid green }
</style>
<div class="layer layer1" style="position: relative; display: block; width: 400px; border: 1px solid black;">
Container
<div class="layer layer2" contentEditable>Edit me</div>
<div class="layer layer3">
<div class="layer layer4" contentEditable>Edit me</div>
</div>
</div>

absolute positioned elements are removed from the flow, thus ignored by other elements
the only way you have is to set the child position to position:relative, in this way it is possible to move it using right,left,top and bottom and also change parent display to display:inline-block

If you want keep the children absolutely positioned, you can use the following script to resize the container : http://jsfiddle.net/6csrV/7/
var layer1 = document.getElementsByClassName('layer1'),
i = 0, len = layer1.length, childHeight;
for(; i < len; i++) {
childHeight = layer1[i].getElementsByClassName('layer')[0].clientHeight;
layer1[i].style.height = childHeight + 'px';
}
document.addEventListener('keyup', function(e) {
if(e.target.className.indexOf('layer2') !== false) {
e.target.parentNode.style.height = e.target.clientHeight + 'px';
}
});

Related

Center Child to Parent When Child is Absolute Positioned [duplicate]

This question already has answers here:
How to center a "position: absolute" element
(31 answers)
How can I center an absolutely positioned element in a div?
(37 answers)
How to center absolute div horizontally using CSS?
(9 answers)
Closed 1 year ago.
Is there a way to position an absolute positioned element centered to its' parent who is relative positioned?
I was thinking if somehow I can calculate the width of the parent, and based on that, center the child? But not sure where to start whether with JavaScript or css.
Here's the codepen for reference
.tooltip {
/*
position the top-left corner of the element
in the center of the parent
*/
position: absolute;
top: 50%;
left: 50%;
/*
move the (positioned) element up and left
by half its own width/height
*/
transform: translate(-50%, -50%);
}
.container,
.tooltip{
border: 2px solid black;
padding: 10px;
}
.container {
position: relative;
width: 80vw;
height: 50vh;
}
<div class="container">
<div class="tooltip">Tooltip</div>
</div>
top and left percentages are percentages of that dimension on the of the parentElement.
Whereas the percentages in translate are relative to the element itself.
This should center your tooltip
const tooltip = document.getElementById('tooltip');
const parentWidth = tooltip.parentElement.offsetWidth;
tooltip.style.left = (parentWidth - tooltip.offsetWidth) / 2 + 'px';
You can use left/right positioning with transform:translateX in order to put element in the center of it's parent. I've made below snippet by using your codepen example:
#container {
border: 1px solid gray;
}
#wrapper {
display: inline-block;
position: relative;
height: fit-content;
border: 1px solid red;
padding: 8px;
}
#tooltip {
border:1px solid green;
position:absolute;
bottom: -24px;
left: 50%;
transform: translateX(-50%);
white-space: nowrap; /* to prevent break the text*/
}
<div id="wrapper">
Trigger Content, Trigger Content, Trigger Content,Trigger Content,
<div id="tooltip">I want to be a centered to my parent</div>
</div>
Edit: I think Thomas Answer contains the better way
Well it depends on your code if you want to do this css only, Here are several cases I came up with after doing my research:
1. If your div has a set size (width & height), According to this page you can take this steps:
Add left: 50% to the element that you want to center. You will notice
that this aligns the left edge of the child element with the 50% line
of the parent.
Add a negative left margin that is equal to half the width of the
element. This moves us back onto the halfway mark.
Next, we’ll do a similar process for the vertical axis. Add top: 50%
to the child
And then add a negative top margin equal to half its height.
In your case it looks like this:
#container {
border: 1px solid gray;
}
#wrapper {
display: inline-block;
position: relative;
height: fit-content;
border: 1px solid red;
padding: 8px;
}
#tooltip {
width: 100px; /* could be set by % aswell */
height: 10px;
border:1px solid green;
position:absolute;
left:50%;
top:50%;
margin-left: -50px;
margin-top: -5px;
bottom: -24px;
}
<div id="wrapper">
Trigger Content, Trigger Content, Trigger Content,Trigger Content,
<div id="tooltip">Center</div>
</div>
2. If it doesn't contain a set value but you want it to be centered anyway:
Not sure if it's a good way but you can use an absolute element inside wrapper before any other content, covering its parent and having its display value as flex and containing justify-content: center as well as align-items: center;
e.g. on your code:
#container {
border: 1px solid gray;
}
.full-flex{
width: 100%;
display: flex;
position: absolute;
justify-content: center;
align-items: center;
}
#wrapper {
display: inline-block;
position: relative;
height: fit-content;
border: 1px solid red;
/* padding: 8px; */ /* I removed the padding so you can get a better understanding of this */
}
#tooltip {
border:1px solid green;
}
<div id="wrapper">
<div class="full-flex">
<div id="tooltip">Wanna be centered</div>
</div>
Trigger Content, Trigger Content, Trigger Content,Trigger Content,
</div>

How to vertically and horizontally center a div using javascript and css margin?

I am currently trying to center a rectangle in an image with only using javascript (no css center properties). However, even if the numbers are right, the showing is wrong.
To do this, I use the following code :
$(document).ready(function() {
$(".img-zoom-container").css("width", $("#myimage").width());
$(".img-zoom-container").css("height", $("#myimage").height());
$("#lens_container").css("width", ($("#myimage").width() - $("#lens").width()));
$("#lens_container").css("height", ($("#myimage").height() - $("#lens").height()));
$("#lens_container").css("top", ($("#lens").height() / 2));
$("#lens_container").css("left", ($("#lens").width() / 2));
});
.img-zoom-container
{
border: 1px solid red;
}
#lens
{
border: 1px solid white;
width: 50px;
height: 50px;
position: absolute;
}
#lens_container
{
border: 1px solid cyan;
position: absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="img-zoom-container">
<div id="lens"></div>
<div id="lens_container"></div>
<img id="myimage" src="https://via.placeholder.com/600x160.png?text=Testing Image" alt="">
</div>
The item I am trying to center is the #lens_container div (appears blue on screen). I also have a white square (#lens div) of size 50px by 50px. I would like to center and to size the blue rectangle in order to have half of the square width at each side of the blue rectangle and same with height. However, as you can see when trying the code, it is not the case although the maths are correct.
I do not know if you can understand my needs, but I would really appreciate help there.
Thanks in advance.
There are 2 issues:
First, position: absolute means to position the item "to its closest positioned ancestor, if any; otherwise, it is placed relative to the initial containing block" (reference). The parent element ".img-zoom-container" is not positioned. The initial container block would be <body>, which has some padding by default.
So your #lens_container is positioned relative to <body> of the iframe, which is probably not what you expected. Moreover, <body> by default has a non-zero padding size. You may see it clearer if you simply use CSS to position everything to top: 0 and left: 0:
body {
background-color: rgba(0, 0, 0, 0.1);
}
.img-zoom-container
{
border: 1px solid red;
width: 600px;
height: 160px;
}
#lens
{
border: 1px solid white;
width: 50px;
height: 50px;
position: absolute;
top: 0;
left: 0;
}
#lens_container
{
border: 1px solid cyan;
width: 550px;
height: 110px;
position: absolute;
top: 0;
left: 0;
}
<div class="img-zoom-container">
<div id="lens"></div>
<div id="lens_container"></div>
<img id="myimage" src="https://via.placeholder.com/600x160.png?text=Testing Image" alt="">
</div>
To have both #lens and #lens_container positioned relative to .img-zoom-container, you have to give .img-zoom-container a "position" value so it can be the "position ancestor":
$(document).ready(function() {
$(".img-zoom-container").css("width", $("#myimage").width());
$(".img-zoom-container").css("height", $("#myimage").height());
$("#lens_container").css("width", ($("#myimage").width() - $("#lens").width()));
$("#lens_container").css("height", ($("#myimage").height() - $("#lens").height()));
$("#lens_container").css("top", ($("#lens").height() / 2));
$("#lens_container").css("left", ($("#lens").width() / 2));
});
.img-zoom-container
{
border: 1px solid red;
position: relative; /** this line **/
}
#lens
{
border: 1px solid white;
width: 50px;
height: 50px;
position: absolute;
}
#lens_container
{
border: 1px solid cyan;
position: absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="img-zoom-container">
<div id="lens"></div>
<div id="lens_container"></div>
<img id="myimage" src="https://via.placeholder.com/600x160.png?text=Testing Image" alt="">
</div>
It's still 1-2 pixels off. That is because you didn't take the border width into consideration (your second issue). You'd get a better result once you clear your head and think how you want the border widths to behave.
Depending on its container, you could just set an ID on your div like:
CONTENT .
Then in javascript, if there is an event to center it, you could make a function like:
function centerDivItem() {
document.getElementById("id1").style.alignContent = "center"
}
And then, as I said, call it from another place.

Have fixed positioned child obtain the same exact width of its parent, when parent's width is declared in percentages?

Most of use knows that fixed elements are never relative to their parent, and are instead relative to the port size, and thus setting their width or height to 100% will make it relative not to their parent, but to the port size. There is a way around this, and that is to use the keyword 'inherit' instead in declaring its width, but there's one problem with this:
#wrap1 {
border: 1px solid black;
position: relative;
width: 250px;
height: 250px;
margin: 0 auto;
}
#fixed1 {
border: 1px solid red;
position: fixed;
width: inherit;
}
<div id = 'wrap1'>
<div id = 'fixed1'> fixed </div>
</div>
^---When the size of the fixed element's parent is declared in pixels, the fixed element matches the size of its parent, however...
#wrap2 {
border: 1px solid black;
position: relative;
width: 50%;
height: 250px;
margin: 0 auto;
}
#fixed2 {
border: 1px solid red;
position: fixed;
width: inherit;
}
<div id = 'wrap2'>
<div id = 'fixed2'> fixed </div>
</div>
^---You don't see the intended effect here, which is that the child will have exactly half of the parent's width, instead of having the same exactly width of the parent, because the width of it's parent is 50%, instead of in pixels. But when the code is isn't run on SO's code snippet, this is exactly what you will see.
Is there a way for a fixed positioned child to have the same exact width of its parent, when its parent's width is declared in percentages?
The reason here is that an element with a fixed position is relative to the viewport - and percentage width measurements are computed relative to the viewport itself. In your percentage example, the parent's width: 50% is computed relative to the body, minus the body's 16px margin. Your fixed element inherits width: 50% from its parent, but the percentage is computed relative to the entire viewport, which completely wraps around the body and its margin (hence the fixed element extends past its parent).
One solution that can give the fixed element its parent's width, is to remove the margin from the body.
body {
margin: 0;
}
#wrap2 {
border: 1px solid black;
position: relative;
width: 50%;
height: 250px;
margin: 0 auto;
}
#fixed2 {
border: 1px solid red;
position: fixed;
width: inherit;
}
<div id = 'wrap2'>
<div id = 'fixed2'> fixed </div>
</div>
change your #fixed2 CSS style as below.
#fixed2 {
border: 1px solid red;
position: inherit;
width: 100%;
margin: 0 auto;
}

Should I use CSS, javascript or both to make this fixed element expand the parent element?

I know that a an element with position:fixed acts like its parent element with position:relative (Or no position specified) doesn't exist, and that's my concern. I've seen the very same question being asked here on StackOverflow, but not the very same problem.
I have a wrapper, an element with relative position, and an element with a fixed position inside the relative element. The element with fixed position should expand the element with relative position as you scroll the page, but what's happening is that when you scroll the page, the element with fixed position will go out of the main container, instead of expanding. How can I make the main element push the container element bellow, instead of getting an offset?
Fiddle
http://jsfiddle.net/T2PL5/515/
Here my code:
CSS
body {
background: #ccc;
}
.wrapper {
margin: 0 auto;
height: 600px;
width: 650px;
background: green;
}
.sidebar {
background-color: #ddd;
float: left;
width: 300px;
height: 350px;
position: relative;
}
.main {
background-color: yellow;
width: 200px;
height: 100px;
position: fixed;
margin: auto;
}
HTML
<div class="wrapper">wrapper
<div class="sidebar"> Sidebar
<div class="main">main</div>
</div>
</div>

Put overlay on document with transparent window

I would like to do something with my document which is quite unique (haven't seen it before) and thus maybe not even possible.
What I would like is to have a div which will overlay everything in the document, maybe give it background black so that nothing is visible. Second I would like to have a small squire window in the overlay which doesn't share the black background, in fact it is somewhat transparent and therefore it would be possible to 'peek' trough that window to see document content. But only the content where this window is. It would be kinda like those "zoom" plugins in which only a small portion is being zoomed, but in this case it would show specific content. Any idea how to create such a thing?
An example of what you can do is the following (it may not be the best but it works)
HTML
<div id='peakview'></div> <!-- This div is your view window -->
<div id='out'>
<div class='overlay'></div>
<div class='overlay'></div>
<div class='overlay'></div>
<div class='overlay'></div>
</div>
The <div> inside of #out will re-size accordingly to the position of #peakview creating the illusion of a full overlay. This can be done with simple css and some calculus.
Mainly what you need is the position of the element on screen.
var h = $(this).offset().top;
var l = $(this).offset().left;
var r = ($(window).width() - ($(this).offset().left + $(this).outerWidth()));
//right offset
var b = ($(window).height() - ($(this).offset().top + $(this).outerWidth()));
//bottom offset
In my example I used .draggable() from jQuery UI to move the div around. And while dragging the 4 divs shown above are adjusting their height and width to fill up the space between #peakview and document border.
An example for the first div
$('.overlay:eq(0)').css({
top: 0,
left: 0,
width: '100%',
height: h //the height is always changing depending on the #peakview .offset().top
});
In this fiddle you will see how the filling divs behave
Another ruff start:
http://jsfiddle.net/XDrSA/
This require some extra work, but it may suit your needs.
HTML:
<div id="yourContent" style="width: 300px; margin:100px auto;">
<input type="button" id="zoom" value="Click to zoom"/>
</div>
<div id="zoomer">
<div id="window">This is your "window"</div>
<div id="overlay_top"></div>
<div id="overlay_left"></div>
<div id="overlay_right"></div>
<div id="overlay_bottom"></div>
</div>
CSS:
body {
margin:0;
padding:0;
}
#zoomer {
height: 100%;
width: 100%;
position: absolute;
top: 0;
display: none;
}
#overlay_top {
height: 20%;
width: 100%;
background-color: black;
position: absolute;
top: 0
}
#overlay_right {
height: 100%;
width: 20%;
background-color: black;
position: absolute;
right: 0;
}
#overlay_left {
height: 100%;
width: 20%;
background-color: black;
position: absolute;
left: 0;
}
#overlay_bottom {
height: 20%;
width: 100%;
background-color: black;
position: absolute;
bottom: 0;
}
#window {
margin: 0 auto;
height: 100%;
width: 80%;
position: absolute;
background-color: rgba(0,0,0,.5);
}
And a piece of javascript:
$('#zoom').click(function() {
$('#zoomer').fadeIn();
});
You may need to stumble with the positioning, and the window will be a fixed size one. Not draggable though.

Categories