Search code examples
javascriptcsshtmlwysiwyg

Extend height to include absolutely positioned children


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.


Solution

  • I found a pure-css solution! In summary:

    1. Set the child elements to position: relative instead of absolute.
    2. 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.
    3. 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>