5

I stumbled upon a difference in layout rendering between Safari and Chrome/Firefox and I don't know which one is "right".

You can check the jsfiddle here

On Firefox/Chrome the layout is as expected, the yellow div is right after the red ones. But on Safari, the yellow div is positioned under the red ones.

After investigating what I did wrong I found out the bug comes from the CSS class E whose property margin-right (value: -11px) is bigger than the width property (value: 10px) for my div.

I think I understand why Safari renders it this way. The width of div of class B is computed as being the sum of the widths of its children as they have the property float: left;.

Here it is widthB = widthB2*2 + widthE + marginRightE + widthC or marginRightE < -widthE so widthB is not large enough to contain each div next to each other.

So my questions are:

  1. Am I right in my understanding of what Safari does?
  2. Why do Chrome and Firefox render differently? Are they just not decreasing the width of the parent div based on a negative margin-right?
  3. Would the proper correction to always have a margin-right lesser or equal to the width of a div in this case?

Thank you!

HTML:

<div class="A">
  <div class="C">
    <div class="B">
      <div class="B2"></div>
      <div class="B2"></div>
      <div class="E"></div>
      <div class="C">
        <div class="D"></div>
      </div>
    </div>
  </div>
</div>

CSS:

.A {
  background-color: blue;
  height: 200px;
}

.B {
  height:100px;
}
.B2 {
  background-color: red;
  height: 100px;
  width: 100px;
  float: left;
}
.C {
  float: left;
}
.D {
  height: 40px;
  width: 40px;
  float:left;
  background-color: yellow;
}
.E {
  height: 50px;
  width: 10px;
  position: relative;
  left: -10px;
  margin-right: -11px;
  background-color: black;
  float: left;
}
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
koopajah
  • 23,792
  • 9
  • 78
  • 104
  • 1
    I encountered something weird in Chrome on Win7. If you inspect the secong `.B2` in WebInspector and deactivate `float: left` and activate it right after, the calculation of the width of `.B` was 239px while it was 240px at the `beginning`. So after some sort of redraw Chrome acts just like the Safari. This does not apply to FF browser. – yunzen Jan 21 '13 at 16:51
  • You have used CSS class `C` twice. You put `float: left` on the class. Does the outer `.C` really need it? – yunzen Jan 21 '13 at 16:54
  • @yuzen: Yes the `float: left` is mandatory in my layout. This is a really simplified version to try to understand what happens and what would be the proper way to correct it. My layout is a bit more complicated than this one. Thanks for looking ! – koopajah Jan 21 '13 at 20:47
  • I just viewed this in chrome and Firefox, and in chrome the yellow box was under the red, but in Firefox it was next to the red. So, my chrome browser is acting like your safari one. Unfortunately, I often have issues like this when coding HTML. Something that shows up one way in chrome looks very different in IE. Or safari. I think that there are some commands that only work for certain browsers. Also, the version of the browser may make a difference. – Ben Green Jan 18 '13 at 11:10
  • Thanks for your reply. Can you tell me which version of Chrome you are using and your OS? I know browsers have different behaviours. What I want is to ensure that my understanding was correct and also find a way to fix this layout in the end! – koopajah Jan 18 '13 at 12:13
  • I am using windows XP and have chrome version 15.0.874.121. – Ben Green Jan 18 '13 at 12:47

5 Answers5

2

I'm not sure what you expect to happen with the CSS in the JS fiddle. You are delving into undefined behaviour. I say this because:

  • 'C' is floated but does not have a defined width. This leads to issues in various browsers depending on the complexity of the layout.

  • None of the floated elements are ever cleared. When floating it is imperative that a clearfix of some description is used, whether it is clear:both, etc.

If you tweak the mark-up and add a clear-fix, you see that the content is always 239px. See http://jsfiddle.net/eaFn9/

However, it seems like the relatively positioned item 'E' and margin is having a negative impact on the width calculation, as Chrome's web inspector seems to always report oddly for the negative margin on this element.

If you play around with this in web inspector you can see it's almost as if the negative margin is the cause of the drop. I think it may be due to a container that does not have a width, and isn't position relative in itself.

How to fix?

Personally, I would want to re-write your layout to include fixed widths on all floats, reduce nesting of floats and clear where possible. It seems overly complex but without a real world use case it's hard to rewrite.

However, It seemed to me that you can wrap 'B2' + 'E' elements in a wrapper that is floated and relatively positioned, then use absolute positioning on 'E' to give the same affect and remove the negative margin.

This is the JSFiddle I came up with: http://jsfiddle.net/jV3Ub/

Hux
  • 3,102
  • 1
  • 26
  • 33
  • Thanks for your detailed answer. I'm trying to understand what you changed and the impact. In your first jsfiddle, the clear leads to the same behaviour in Chrome/Safari but Firefox still renders the `E` on the first line, why is that? – koopajah Jan 23 '13 at 10:06
  • I understand that without a real layout it's hard to understand but the real layout is more complicated. I'm building a grid with parents that can have multiple children line which can have multiple children lines, etc. I was trying to avoid computing width dynamically **everywhere** and only set them on div of class `B` and have the whole grid adapt automatically. Then I encountered this strange behaviour triggered by the margin-right incorrectly set – koopajah Jan 23 '13 at 10:07
  • I thought of this solution as well but opted for div class E inside the right most div class B then float right. Your's accounts for the content under E in B2 drifting behind B2 instead of wrapping, however, absolute positioning is always less fluid. Good answer. – user1934286 Jan 24 '13 at 08:49
  • @koopajah I'm not sure why, but the nested floats and negative margin combined are definitely the culprits. If you think it is a webkit bug, then you could always file a bug. Personally I would stay away from such complexities if possible. – Hux Jan 24 '13 at 10:39
  • 1
    @fredsbend - I would have preferred to rewrite and start again but it's the only thing I could think of without re-writing. The relative positioning seems hackish to me without a relative parent. I tend to avoid positioning elements like this. – Hux Jan 24 '13 at 10:39
  • @MiG Why does that seem hackish. Isn't that what relative positioning is for? Moving stuff out of the flow but still affect it? Anyway koopajah said he can't move the divs so I came up with this http://jsfiddle.net/uryJJ/33/ very close to yours. – user1934286 Jan 24 '13 at 11:13
  • 1
    @fredsbend To me it seems hackish because in your example the parent 'C' will occupy 250px worth of space even though it's true width is only 240px due to the relative positioning. – Hux Jan 24 '13 at 11:40
  • @MiG : Thanks for taking the time with my problem and your different explanations. I corrected some of the issues by ensuring the margin-right is always equal to the width in my case. I also added some clear:both; in a lot of places for the layout to not break when one element breaks (as it is a grid the rest is rendered properly). I still have a lot to grasp with positionning/float and div's size – koopajah Jan 24 '13 at 17:16
  • @MiG It's only a hack if you use that to fix an error made elsewhere. Not anymore hackish then the original code with negative margin we can agree? I see what you are saying about C and its width but that doesn't really matter in this case because there is no specific width to C. I made some edits to my answer you might be interested in. – user1934286 Jan 24 '13 at 19:00
  • @fredsbend Yes we agree on that. The rules on collapsing margins seem rather obscure but there is certainly a lot of rules defined for floated + relative + negative margin elements, potentially why we see the problems. – Hux Jan 24 '13 at 19:20
1

Sorry, this is not really an answer but it's too long to make it a comment... Anyway, it took me a minute to figure this out.

I used Firefox 19 on Mac OS X 10.8.2, Chrome 24.0 (Mac) and Safari 6.0.2 (Mac as well). Using the web inspector tools, I realized the divs are not computed the same way indeed. I suck at calculations, but I took the time to sit down and look at this thoroughly, and I do understand Safari's calculations the same way you do.

In Safari, it seems that div B isn't wide enough to contain the yellow div (C) so it seems to reject it to the bottom. For the record, in my tests, I see the yellow div to the right of the red div in FF and Chrome, while Safari shows it right underneath the red, and to the upper left. I am not sure this will help, but I can only recommend you to use the web inspector tools now integrated to all modern browsers to debug this.

I'm not sure about why this happens, all I know is that by only changing the width of E by 1px, like so:

.E {
  height: 50px;
  width: 11px; /* added 1px to this property */
  position: relative;
  left: -10px;
  margin-right: -11px;
  background-color: black;
  float: left;
}

it displays correctly in Safari.

Dyn
  • 395
  • 3
  • 18
  • Thanks for taking the time to answer! The thing is that you found out exactly what I explained in my question : when having a margin-right of type -Xpx and a width of Ypx and X > Y then the layout is "broken" on Safari. So increasing the width effectively corrects the issue – koopajah Jan 19 '13 at 08:58
  • I think this proves that safari will not auto adjust width if the margin exceeds it. – user1934286 Jan 24 '13 at 08:54
1

Make the following changes to classes .D and .E:

.D {
  float:left;
  height: 40px;
  width: 40px;
  background-color: yellow;
  margin-left: -11px;
}
.E{
  height: 50px;
  width: 10px;
  position: relative;
  left: -10px;
  background-color: black;
  float: left;
}

DEMO: http://jsfiddle.net/uryJJ/22/

I hope this helps!

Dom
  • 38,906
  • 12
  • 52
  • 81
1

SECOND EDIT:

I think we should link these two questions: https://stackoverflow.com/questions/4989930/css-negative-margin and why use negative margins? to this one.

Also See the W3C spec on margin: http://www.w3.org/TR/CSS2/box.html#margin-properties. Section 8.3.1 Might actually explain what is going on with your sample. A collapsing margin issue not rendering correctly in Safari.

ORIGINAL POSTING:

So my questions are:

1) Am I right in my understanding of what Safari does. Why do Chrome and Firefox render differently? Sounds like that might be it, but, really, who cares? You are not getting the results you want. You should change your code unless you don't care about Safari users.

2) Are they just not decreasing the width of the parent div based on a negative margin-right? Probably, but again, not really important.

3) Would the proper correction to always have a margin-right lesser or equal to the width of a div in this case? I would say yes. To fix the issue and get the results you want I would move the div with class E inside the right most div with class B2. Then float E to the right and remove the position, left and margin-right attributes.

.E {
height: 50px;
width: 10px;
background-color: black;
float: right;
}

http://jsfiddle.net/uryJJ/32/

FIRST EDIT

.D {
height: 40px;
width: 40px;
float:left;
background-color: yellow;
position:relative;
left: -10px;
}
.E {
height: 50px;
width: 10px;
position: relative;
left: -10px;
background-color: black;
float: left;
}

http://jsfiddle.net/uryJJ/33/

Community
  • 1
  • 1
user1934286
  • 1,732
  • 3
  • 23
  • 42
  • Who cares? I do. Understanding this kind of problems makes sure I won't encounter them again later, that's the whole point of coding for me! I care about Safari users too and I corrected my layout to avoid these issues but I still wanted to understand what happens – koopajah Jan 24 '13 at 08:55
  • And moving the div of class `E` is not possible in my current "real" case. I've considered it but it messes with a lot of other stuff later down the road. – koopajah Jan 24 '13 at 08:56
  • @koopajah Ok. I understand your analytic nature. Though, I think it is clear that the problem is that a negative margin value is unpredictable, especially if it exceeds the value of the width. – user1934286 Jan 24 '13 at 09:02
  • That looks like a "hack" to add a `left: -10px;` to the div of class D. I can't really do it in my case. But thanks for proposing this. – koopajah Jan 24 '13 at 17:26
  • @koopajah Thanks for the up. See my second edit. I know you already selected an answer but this is interesting. – user1934286 Jan 24 '13 at 18:51
  • Thanks a lot for the links, I'll try to read and wrap my head around what's happening and what it means exactly. As pointed by others the problem is not only Safari in the end, Chrome will render differently if you play dynamically with the margin inside the inspector too. – koopajah Jan 24 '13 at 19:29
0

Sorry, I might be beating this to death but this fixes it:

.E {
height: 50px;
width: 10px;
margin-left: -10px;
background-color: black;
float: left;
}

http://jsfiddle.net/uryJJ/35/

I was not a fan of negative margin values until just now.

user1934286
  • 1,732
  • 3
  • 23
  • 42
  • Yeah but that's kind of what I explain in my question. The issue comes from having a margin-right (or left) whose value (in absolute) is larger than the width of the div – koopajah Jan 24 '13 at 19:26