How to prevent strange element misalignments - html

I'm trying to lay out some elements in a line such that they all have the same top and bottom vertically.
If I create a button and text input with the same specified size, the button actually has a slight margin at the top and bottom which make it appear smaller than the text input. When I inspect the element in Chrome developer tools, it doesn't show this extra space as part of the actual element!
<button style="height:160px">
test
</button>
<input style="height:160px" />
Fiddle
Why is this happening and how do I get rid of the extra space around the button?
Now if I remove the text from the button, suddenly the second element get pushed way down! I see the same thing if I put a space (e.g. ) in the input.
<button style="height:160px">
</button>
<input style="height:160px" />
Fiddle
Again, what's going on here and how do I fix it?

The box model and box-sizing property are the reason for the slight size difference on your first example. It's the way browser's calculate the width and height based on margins, padding, etc.
Set the box-sizing property to border-box and they line up just fine
input {
box-sizing: border-box;
}
<button style="height:160px">
test
</button>
<input style="height:160px" />
As for your second example, it's the vertical align property that comes into play. It affects inline elements and the default is baseline. Set it to something like top or middle and they align as you would expect. Throw in the same box-sizing as the previous example and they become the same height:
input {
vertical-align: top;
box-sizing: border-box;
}
<button style="height:160px">
</button>
<input style="height:160px" />

First Question:
There isn't any extra space around the button, but there is extra space inside the input tag, in the form of padding, which causes its effective height to exceed 160px.
If you want the total height of the input to be 160px, including padding, set box-sizing: border-box on the input (arguably, this should be the default behavior).
Second Question:
To align the button without any text inside it, set vertical-align: bottom; (or top, or middle) on the button.
For explanation: the default vertical-align value of baseline will try to align the baseline of text inside the element with the text in the parent element, but if there isn't any text inside the element, will instead try to align the bottom margin edge, which means that the bottom of the button ends up around the middle of the input.
Code with Both Fixes:
<button style="height:160px; vertical-align: bottom;">
</button>
<input style="height:160px; box-sizing: border-box;"/>

The other answers explain box-sizing, and how to align the elements with vertical-align.
Another approach to the alignment is to wrap the elements in a flex container. You can also set the height on the container instead of both the elements.
Example:
div * {
box-sizing: border-box;
}
div {
display: flex;
height: 160px;
}
<div>
<button></button>
<input>
</div>
<br>
<div>
<button>Content</button>
<input>
</div>

Related

Why is the floating element not detached from the document flow when its container has no height?

<form style="background-color:pink; height:100px">
<label for="button-test">Add your name here</label>
<input type="text" id="button-test" name="button" placeholder="add your name here">
<div style="display:inline-block; height:">
<input class="button-image" type="image" src="submit.png" alt="image button" width="40" height="40">
</div>
</form>
and CSS:
.button-image {
margin-left:20px;
float:right;
margin-top:px;
}
If i don't add height to the div container, and try to add margin-top to the child-element "button-image", all 3 elements move down. But if i add height to the div container of the button-image, now the margin-top only moves the button-image element, while the other 2 stay on place.
I made the "button-image" element floating on purpose in order to be able to move it down a little bit without moving the other 2. Since all 3 are inline-block elements, setting a margin-top on either one of them, would move all 3 down, because they need to maintain the doc flow. I thought making the button-image element floating will take it out of the doc flow and i will be able to move it freely without effecting the others. But i discovered that, if the div container does not have a height, all 3 elements again move down when i set a margin-top to the floating "button-image" element. Why is that? Isn't that element supposed to be floating, and therefore be out of the document flow, and therefore not effect the others? Why is setting a margin-top makes the other elements move with it? If i add just 1px height to the div container, now only the floating element moves.
Can somebody explain what difference does the height make in this case?
From the CSS 2.2 spec:
10.6.7 'Auto' heights for block formatting context roots
... if the element has any floating descendants whose bottom margin edge is below the element's bottom content edge, then the height is increased to include those edges.
This is the same rule as makes overflow:hidden on a block box contain a float, similar to a clearfix.
All inline-block elements are block formatting context roots, and so this rule applies to them when you don't specify a height, because the default height is auto.
Then at the very end of Section 10, the spec says:
The baseline of an 'inline-block' is the baseline of its last line box in the normal flow, unless it has either no in-flow line boxes or if its 'overflow' property has a computed value other than 'visible', in which case the baseline is the bottom margin edge.
In your example, the inline-block contains only the float so it has no in-flow line boxes and therefore its baseline is its bottom margin edge. It has zero bottom padding, border, and margin, so its baseline is also its bottom content edge.
Then your label and text input vertically align their baseline to that inline-block bottom content edge, so the lower the bottom of the float is, the lower those elements will be to keep them vertically aligned.
But if you specify a height to the inline-block, its bottom content edge won't be moved down to enclose the float - i.e. the float will overflow the inline-block. The label and text input still vertically align with the bottom of the content box of the inline-block, but that's only as tall as the height you have specified.
To avoid this happening, the usual technique applies of breaking the baseline vertical alignment between the inline elements, by for example, setting the inline-block to vertical-align:top

Why a nested span inside a div does not follow line-height rule for that div?

I have a div with font size of 88 and line height of 88. The text inside the div has a height taller than 88. Why is this?
<div style="font-size:88px;line-height:88px;">I need <span sytle="color:red;">videos</span></div>
If you open up the element inspector and highlight the parent div, it is 88px tall. However if you highlight the text "I need" and the nested span, the height is 101px. This remains true even if you set the line-height on the span itself:
<div style="font-size:88px;line-height:88px;">
I need <span style="font-size:88px;line-height:88px;color:red;">videos</span>
</div>
See attached repl: https://repl.it/#teeej/ReliablePunctualRam
<span> is, by default, an inline element.
If you expect it to behave like an inline block element, you have to give it a display value of inline-block and it will have a height of exactly 88px:
div > span {
display: inline-block;
background-color: rgba(255,0,0,.1);
}
<div style="font-size:88px;line-height:88px;">
I need <span style="font-size:88px;line-height:88px;">videos</span>
</div>
For a better understanding of the implications of display property, I recommend the Candidate Recommendation. And here's the current (official) Recommendation.

Strange behavior of an input html element with display: block

I'm trying to make some html form with help of bootstrap. Some of my inputs must have no gap from left or right side. But bootstrap .col-XX-Y blocks have gutter via paddings from left and right. So my idea was to use negative margin for my input elements, and display: block. And here I'm stuck.
Please refer to this codepen example. I was faced with several strange things for me:
Why input with display: block doesn't fill all it's parent
container, like div does? It fills the container only with: width:100%; (comment width for red-bordered input in codepen example)
Why if I'm apply negative margin-left to compensate parent container's
left padding, my input shifts to the left, but keeps it's original width (like if left css property was used). Doesn't it have to behave
like a block element (e.g. div): shifts to the left and keep
filling of all width of it's parent, excluding right padding?
When I'm apply negative right margin for my input to compensate parent's right padding, then nothing happens (look my example, compare orange div
with red input). Why? What about of a behavior like block element?
If this is normal behavior, can you give me some link to html standard docs with explanations of that.
If you don't want the padding on a grid parent element to effect its children, surround all its children elements in a block element with a class of row.
Bootstrap input elements are meant to span the whole width of there parent elements even without display block style attribute.
<div class="col-md-12">
<div class="row"> <!--this is what you need -->
</div>
</div>
full example code
<div class="col-md-12">
<div class="row">
<input type="text" placeholder='I\'m some damned input' />
</div>
<div class="row">
<div>I am some div</div>
</div>
</div>
Form elements do not behave the same way as regular block level elements. When you display an <input> field as block it will not fill the full width.
For this reason you need to make give the element width: 100%. This is how Bootstrap styles form elements.
Like any other block element, giving it a width of 100% will allow it to fill the width of its container. When you apply a negative margin-left, the width will still be the same (100% = containers width) which will cause the gap to appear.
I would suggest wrapping the <input> field in a <div> and apply the negative margin to that instead:
.wrap {
margin: 0 -20px;
}
.wrap input {
width: 100%;
display: block;
}

Adding line-height to one DIV displaces another DIV [duplicate]

I have the following:
<div>
<div style="display:inline-block; ">div_1</div>
<div style="display:inline-block; line-height:20px;">div_2</div>
</div>
Why does having a line-height property set for the second div also effects the first div? And how to correct for this i only need the second div to be effected by line-height because I need to specify a different line-height for the first div. Thanks in advance.
document.getElementById('go').onclick = function(e) {
document.getElementById('div_2').style.lineHeight = '30px';
};
<button id="go">Go</button>
<div>
<div style="display:inline-block;" id="div_1">div_1</div>
<div style="display:inline-block; line-height:24px;" id="div_2">div_2</div>
</div>
With the test case, it's now crystal clear.
Add vertical-align: top to the first div:
<div style="display:inline-block; line-height:24px; vertical-align: top" id="div_1">div_1</div>
Fixed version: http://jsfiddle.net/my6Su/5/
Read this to understand the relationship between display: inline-block and vertical-align:
http://blog.mozilla.com/webdev/2009/02/20/cross-browser-inline-block/
Also useful, for a visual demonstration:
http://www.brunildo.org/test/inline-block.html
Firstly, the effect of line-height is only on inline elements. When line-height is applied to block, inline-block or any other type of element that is not inline, the effects are on the inline descendant elements only.
Secondly, in a line-box (an abstract box enclosing inline elements in a line), all the inline elements are aligned along the baseline. When you change the line-height for the second div, it adds half-leading at the top (and bottom) of that inline-element. And top half-leading pushes the baseline lower, which in turn moves the first div lower.
I am not exactly sure what you are trying to achieve, but, I would recommend either using the vertical-align property or just use position relative.
<div>
<div style="display:inline-block; line-height:10px;">div_1</div>
<div style="display:inline-block; line-height:20px;">div_2</div>
</div>
try this. this will work.

Why does setting the margin on a div not affect the position of child content?

I'd like to understand a little more clearly how css margins work with divs and child content.
If I try this...
<div style="clear: both; margin-top: 2em;">
<input type="submit" value="Save" />
</div>
...the Save button is right up against the User Role (margin fail):
Margin Fail :( http://img697.imageshack.us/img697/8459/nomargin.jpg
If I change it to this...
<div style="clear: both;">
<input style="margin-top: 2em;" type="submit" value="Save" />
</div>
...there is a gap between the Save button and the User Role (margin win):
Margin Win :) http://img20.imageshack.us/img20/1973/yesmargin.jpg
Questions:
Can someone explain what I'm observing? Why doesn't putting a margin on the div cause the input to move down? Why must I put the margin on the input itself? There must be some fundamental law of css I am not grasping.
This would be because the the div doesn't have an element to "push itself away from". It would seem that the select that comes before the div is floated. This causes it to be taken out of the normal page flow, and it doesn't act as reference for margin calculations anymore. The div is clearing the float, i.e. it drops below it, then tests if there's a 2em margin to the next element above it that is within the same "flow". Apparently there is, so it doesn't move down any further.
Setting the margin on the submit on the other hand is very clear cut, since the frame of reference for it is the parent div.
Putting a margin on an element affects that element's margin only. Are you expecting it to be inherited or something? It isn't. Perhaps you're thinking of padding? Try:
<div style="clear: both; padding-top: 2em;">
<input type="submit" value="Save" />
</div>
See Box Model:
Another thing to note about margins is that they merge. So:
<div>one</div>
<div>two</div>
with:
div { margin: 1em; }
will only result in a 1em gap between them not 2em. See 8.3.1 Collapsing Margins:
Vertical margins may collapse between
certain boxes:
Two or more adjoining vertical margins of block boxes in the
normal flow collapse. The
resulting margin width is the maximum
of the adjoining margin widths. In the
case of negative margins, the maximum
of the absolute values of the negative
adjoining margins is deducted from the
maximum of the positive adjoining
margins. If there are no positive
margins, the absolute maximum of the
negative adjoining margins is deducted
from zero. Note. Adjoining boxes
may be generated by elements that are
not related as siblings or ancestors.
So one possible explanation for what you're seeing is that the element preceding your div already has a margin (at the bottom), which is why it's not being pushed down by adding a margin to your div.
Basically put borders around things and you should see what's happening.
What's happening in the first example is that the floating elements above the button is floating outside it's parent element.
The margin is working just fine, but it's not between the floating elements and tbe button, it's between the floating element's parent element and the button. The parent element doesn't have any non-floating elements, so it's height is zero, and the floating elements overlap the margin.
In your first example, you are setting a margin on the DIV itself. Think of the DIV as a block holding the nested elements. In this case, the nested element would be the button. If you do the following as you did:
<div style="clear: both; margin-top: 2em;">
<input type="submit" value="Save" />
</div>
You are not affecting the child elements style. Although the relative position of your button may change, your are not actually changing the style of the child element. Now by doing:
<div style="clear: both; margin-top: 2em;">
<input type="submit" value="Save" />
</div>
You are now setting the child elements margin. This will have a different result. Setting this child elements margin will not have an affect on the parent element (the DIV).