When the child of a div contains margins, those margins are not included in the calculation of div.offsetHeight
. Except, however, when you do certain things to the parent div, including giving it 1px of padding.
The code below causes the reported height of the div to jump from 38px to 83px (the "right" size, including child margins). Adding a border to the element has the same effect.
What is going on here? Is this defined behavior? Also, is there anyway to get the actual height of the element (including child margins, but not its own margins) without this 1px hack?
(The context for this question is that I was resizing an iframe on window resize, and the calculations only worked properly when the 1px padding was included.)
var hasPadding = false;
var alertDivHeightButton = document.getElementById('2');
alertDivHeightButton.onclick = alertDivHeight;
var togglePaddingButton = document.getElementById('3');
togglePaddingButton.onclick = togglePadding;
function alertDivHeight () {
var div = document.getElementById('1');
alert("width of div: " + div.offsetHeight);
}
function togglePadding () {
var div = document.getElementById('1');
if (hasPadding) {
div.style.padding = '0px';
togglePaddingButton.value = "Add 1px padding";
} else {
div.style.padding = '1px';
togglePaddingButton.value = "Remove 1px padding";
}
hasPadding = !hasPadding;
}
<div id="1">
<h1>Hello, world</h1>
</div>
<input type="button" id="2" value="Alert div height">
<input type="button" id="3" value="Add 1px padding">
As Hashem pointed out in a comment, the problem is the collapsing margins. The parent div
has nothing between it and its parent, so the margins of the div
's child h1
become its margins. If the div
has a padding or a border, this prevents the margin collapse.
In order to make the margins not collapse, we can use the :before
and :after
pseudo-elements on the parent div
:
#a1:before, #a1:after{
content: '';
display: table;
}
This will slightly alter the position of the child element, but it will keep the height consistent regardless of padding and cause the child margins to be included in div.offsetHeight
I found this technique in another answer here.