Trung Nguyen
Trung Nguyen

Reputation: 529

Why is floated div being blocked from wrapping to container edge?

I have 3 <div> elements floated left inside a <div> wrapper. When the screen narrows, the 3rd <div> should wrap around and position itself under the 1st <div>. What I'm experiencing, however, is that the 3rd <div> is wrapping only as far as the 2nd <div>, and I can't figure out why. Please check my CSS and point out where I'm going wrong.

body {
  background: #d2e1ff;
  font-size: 80%;
  font-family: Verdana,Arial,sans-serif;
}
#step2 {
	min-width: 150px;
	max-width: 600px;
}
label {
  font-size: 80%;
}
input[type="text"] {
  width: 98%;
}
.element {
  float: left;
  margin-right: 3%;
  margin-bottom: 1%;
}
.element label {
  display: inline-block;
  width: 100%;
}
.column1 {
  width: 30%;
}
.column2 {
  width: 40%;
}
input#day,input#month {
  margin-right: 2%;
  width: 2em;
}
input#day,input#month,input#year {
  color: #999999;
  width: 3em;
}
@media all and (max-width:400px){
	.element {
		width: 100%;
		display: block;
	}
}
<div id="step2">
  <div class="element column2">
    <label for="telephone">Telephone number</label>
    <input id="telephone" type="text" name="telephone">
  </div>
  <div class="element column2">
    <label for="gender">Gender</label>
    <select id="gender" name="gender">
      <option value="">Select Gender</option>
      <option value="m">Male</option>
      <option value="f">Female</option>
    </select>
  </div>
  <div class="element column1">
    <label for="day">Date of birth</label>
    <input id="day" type="text" name="day" value="dd">
    <input id="month" type="text" name="month" value="mm">
    <input id="year" type="text" name="year" value="yyyy">
  </div>
</div>

Upvotes: 3

Views: 178

Answers (2)

Michael Benjamin
Michael Benjamin

Reputation: 372109

This is interesting behavior. You're right: it is reasonable to think that the third div should wrap all the way back and position itself under the first div in column 1. That's how floats are supposed to work.

Here's a simple demo. Three boxes, floated left. Just like your layout. When you adjust screen width they wrap as expected.

DEMO

And this is what you're experiencing:

DEMO

The solution to the problem is very simple:

Add a height rule to each div.

.element {
    float: left;
    margin-right: 3%;
    margin-bottom: 1%;
    height: 50px; /* This solves the problem */
}

DEMO

Here's what's happening...

It seems that the padding from the Telephone input field is making div box #1 slightly taller than the Gender box. This extra height is literally blocking div box #3 from shifting to the edge of the container.

In this image, the Date of Birth box, which is floated left, is unable to wrap all the way to the container edge. The Telephone div is blocking it.

enter image description here

If we give the telephone input a padding: 0, the height is reduced and the obstruction is removed:

input[type="text"] { padding: 0; }

enter image description here

But who the hell wants text fields with no padding?

There is a clean, simple and effective solution to this problem, and it doesn't involve removing the padding from form inputs: Add a height rule to the div class (as described above).

.element { height: 50px; }

enter image description here

DEMO

A second possible solution is to give div #2 enough bottom margin to clear the height of div #1.

<div class="element column2" style="margin-bottom: 10px">
<!-- not necessarily recommending inline style; just for demo purposes -->

One side note...

Collapsed Container
Your parent container (id="step2") isn't wrapping anything, because you've floated the child elements – which takes them out of the normal flow – but you didn't "notify the parent". So your container div has 0 height since it has no content.

To observe this behavior yourself, highlight the div with id="step2" in Chrome Dev Tools or add a border around it.

In your code the top and bottom borders of parent container id="step2" are touching, because the box has no content and, therefore, no height.

enter image description here

From a practical perspective, this means that many styles will be lost on this container (try adding a background color to #step2).

There are several ways to address this issue – known as clearfix methods. In this case I've used the overflow property. Add overflow: auto to the container div.

#step2 { overflow: auto; }

enter image description here

DEMO

Hope this helps. Good luck!

Upvotes: 4

guest
guest

Reputation: 6718

There's no clear: left rule on the third <div>. The layout only advances to the bottom of the second <div>, which is slightly shorter than the first. The third div floats left, but the first one is, like, in the way. So it doesn't go all the way out to the margin.

If you're interested in how floats work, see Introduction to floats.

A floated box is shifted to the left or right until its margin edge touches the containing block edge or the margin edge of another float.

(emphasis added)

Here I've added some background colors to visualize the problem. I've also added clear: left to a new new-row class.

body {
  background: #d2e1ff;
  font-size: 80%;
  font-family: Verdana,Arial,sans-serif;
}
#step2 {
	min-width: 150px;
	max-width: 600px;
}
label {
  font-size: 80%;
}
input[type="text"] {
  width: 98%;
}
.element {
  float: left;
  margin-right: 3%;
  margin-bottom: 1%;
}
.element label {
  display: inline-block;
  width: 100%;
}
.column1 {
  width: 30%;
  background-color: lime;
}
.column2 {
  width: 40%;
  background-color: yellow;
}
.new-row {
  clear: left;
}
input#day,input#month {
  margin-right: 2%;
  width: 2em;
}
input#day,input#month,input#year {
  color: #999999;
  width: 3em;
}
@media all and (max-width:400px){
	.element {
		width: 100%;
		display: block;
	}
}
<div id="step2">
  <div class="element column2">
    <label for="telephone">Telephone number</label>
    <input id="telephone" type="text" name="telephone">
  </div>
  <div class="element column2">
    <label for="gender">Gender</label>
    <select id="gender" name="gender">
      <option value="">Select Gender</option>
      <option value="m">Male</option>
      <option value="f">Female</option>
    </select>
  </div>
  <div class="element column1 new-row">
    <label for="day">Date of birth</label>
    <input id="day" type="text" name="day" value="dd">
    <input id="month" type="text" name="month" value="mm">
    <input id="year" type="text" name="year" value="yyyy">
  </div>
</div>

Upvotes: 0

Related Questions