A few weeks ago Andrei raised an interesting question on one of my older posts. The post shows how to create a navigation bar. Andrei noticed the submenu was positioned absolutely, but none of its ancestor elements had positioning applied. Why then did the submenu display directly below its parent link in the menu?
I honestly didn’t know. Like Andrei my expectation was the submenu should be positioned relative to the html element given the absence of other positioned elements and should appear in the top left corner of the browser, but it didn’t.
The reason turned out to be very simple, though it took some investigation to find it. I’ll spare you the false leads and just explain what’s going on.
Containing Blocks Set Positioning Context
As a quick review when you use css positioning on an element its position is relative to some containing block. That containing block will be the nearest ancestor element that also has positioning (other than static) applied. If there is no ancestor with positioning applied the containing block becomes the initial containing block, which is the html element.
In the post I was using the Suckerfish system to create a drop down menu. I’ve been using it to create drop downs for as long as I can remember. You probably have too. By default you position your submenu far off the page, usually -999em to the left. On hover you change the value of left to auto and the submenu appears directly below its parent menu item.
At first glance the submenu seems to be ignoring the above information about containing blocks. None of its ancestors have positioning set implying it should be positioned relative to the html element or the browser window.
At least that’s what Andrei and I both thought should happen. Our mistake was in how we interpreted “auto” to work.
How Should a Browser Interpret Auto?
We were both assuming that a value of auto was the same as a value of 0 and so setting left: auto was equivalent to left: 0. There are times when a css value of auto equates to 0. This is not one of those times though.
When an element has been set to position: absolute, its position (and possibly size) is specified with the ‘top’, ‘right’, ‘bottom’, and ‘left’ properties. These properties specify offsets with respect to the box’s containing block. For non-replaced elements, the effect of the auto value (for top, right, bottom, or left) depends on which of these other related properties have the value ‘auto’ as well.
That last part is the key. By definition auto isn’t 0. It might be, but it depends on the values set on the other properties.
The Suckerfish drop down sets and then changes the “left” property, but it leaves the other 3 properties as auto. When we initially set left to -999em the right value comes along for the ride and the submenu is located way off the page. On hover when we change left to auto we have the condition where all 4 values are auto.
An article I came across on auto positioning for absolutely positioned elements explains what happens as does this article from Dev.Opera on absolute and fixed positioning. The quote below is from the latter.
The default value for the top, right, bottom and left properties is auto, which means the absolutely positioned box will appear exactly where it would have had if it wasn’t positioned. Since it’s removed from the flow it will overlap any elements in the normal flow that follow it, though.
You can see this in action in the images above and below this section. Above is where the menu appears when hovering over its parent link. Here left (as well as right, top, and bottom) are set to auto.
Below is the same menu when I remove positioning on the submenu. The submenu is located in the same place as above. The top level menu items are in different locations, because the sub menu is now again in the document flow, but the submenu itself is located in the same place in both images.
It’s obvious once you think about it. If the default of auto was 0, then an element with all 4 sides set to 0 would need to stretch to each edge of its container. There are times when we rely on this stretching. It’s part of one method for vertically centering elements.
That’s clearly not what’s happening here. When all 4 sides (or really when either of the 2 opposing sides) hold values of auto it’s up to the browser to decide how best to locate the element.
We rely on this to horizontally center block level elements with margin-left: auto and margin-right: auto. In this case the margin is split equally and the element centered. In the case of the drop down, a child list gets displayed exactly where it would have been located if no positioning were applied.
Some of you might be thinking why am I wasting time talking about something so obvious. It’s obvious in hindsight, but it certainly wasn’t obvious to me before digging into containing blocks and how auto-positioning works. Since Andrei raised the question, I assume it wasn’t obvious to him either. And if it wasn’t obvious to either of us, I assume there are others who would also say it wasn’t so obvious to them.
Sometimes we accept that a technique works without taking the time to understand why it it works. When asked why we’re at a loss to answer. It’s worthwhile to figure out why and better understand what’s happening.
I’ll no longer take the auto value for granted on any property and assume it equates to what I initially expect. Instead I’ll think about how it will be computed and what will happen based on other possible auto values on related properties. More than likely it will usually behave exactly as I’ve always assumed, but I suspect there will be a few times where it doesn’t and in not behaving as expected it might allow for some interesting results.
Download a free sample from my book, Design Fundamentals.
Thanks for the article explaining this arcane subject. It would have been even more understandable if you’d used “it’s” to mean “it is,” and “its” to be the possessive of “it.” I was stopped in my tracks trying to decide what you meant when the wrong word was used. Sorry to complain, but thought others might have this problem, too. Thanks again.
Thanks Lily. I’m glad you enjoyed the article.
Wow! I was awful with my its and it’s in this post. I swear I do know the difference. I’m not sure how I screwed it up so much here.
I think I caught all incorrect uses and fixed them. Hopefully others won’t have the same problem following along. Sorry about that.
No need to apologize for complaining, by the way. It was completely my bad. I’m glad you let me know so I could fix things. Thanks.
Thanks for the summary Steven. These subtle factors are exactly what can save the day when designing or debugging positioning issues. When there are so many conditional factors behind how an element’s position renders, it’s always illuminating to understand the theory behind rules-of-thumb.
Thanks Thor. I think I’ve always taken the auto value for granted. Most of the time I explicitly set a value and so don’t think about it, but at times I forget it’s still set on some properties and it doesn’t necessarily work the same way across all of them.
Auto positioning definitely tripped me up. After looking into it, it makes perfect sense. It wasn’t what I expected though and I figured others probably expected the same thing I did.
Thanks Apollo. I’m glad I could help make things clearer. The -999 trick is a nice way to hide elements.
It wasn’t obvious, in fact. Glad I found this from the CSS weekly newsletter, nice explanation and useful links, too.
Good to know. I guess what seems obvious to me, may not always be obvious to someone else. Thanks for the perspective.
Absolutely not obvious 🙂
I started my first widespread CSS project a few weeks ago while converting a table-based layout to CSS with media queries and, the sometimes missing logic of positioning, still gets my head spinning once in a while when an object just won’t stay where I want it to.
Also I still can’t figure out if it’s possible to absolutely position an element according to its parent’s parent(?)
By the way – always be careful when positioning elements off-screen and don’t do it if you don’t need to. GoogleBot is smart, but hiding elements like that can be a SEO-disaster. I’m not saying it’s always bad practice but it can be considered black-hat if used extensively.
Great article – thank you!
Guess this topic wasn’t as obvious as I initially thought. Good to know for those topics I don’t cover thinking they’re just as obvious.
You can position an element according to the parent’s parent. To do that you wouldn’t any any kind of positioning on the parent. So
Grand Parent – has some kind of positioning
Parent – has no positioning
Child – can be positioned relative to Grand Parent.
I would stress over positioning things off screen. Google isn’t going to object. It’s more about what you’re positioning off screen. If anything we’re going to see much more content positioned off screen now that off canvas navigation patterns are becoming popular with mobile devices.
Great article, building my own site (getting rid of wordpress) I ve had to learn the hard way. This article has helped me get my head around why I add other peoples CSS recommendations, which often work but never know how. I;m educating my self big time at the mo and this auto explanation on the sub menu helps:-)
I’ve noticed a lot of people show you how to implement a specific technique, but don’t always help you understand how or why it works so it’s difficult to apply to other cases. I try to go to deeper so you can walk away and apply what I’ve talked about to whatever problem you’re trying to solve. Glad to know the deeper look helps.