Reputation:
Is it possible to position an element horizontally in relation to one element and vertically to another using only CSS? I am trying to add a dropdown menu to my navbar. However, the submenu does not align properly. I want to position element A so that its left edge aligns with element B's left edge, but I want element A's top edge to be aligned with element C's bottom edge.
Essentially, I want it under header, aligned with list element (image edited):
I have the following code:
#header{
padding: 0.125em;
background: #eee; /* Styling for clarity */
}
nav{
display: inline; /* Makes nav inline */
}
h1{
display: inline; /* Makes the main title inline */
}
#header ul{
padding:0; /* Clears the space around the list */
display: inline; /* Makes lists inline */
}
#header li{
display: inline; /* Makes list items inline */
list-style-type: none; /* Removes bullets */
}
#header ul>li>.submenu>li{
display: block; /* Makes submenu list items behave normally */
}
#header ul>li>.submenu{
display: none; /* Hides the submenu */
position: absolute; /* Enables positioning */
top: 100%; /* Position directly under - SHOULD BE UNDER HEADER */
left: 0; /* Align left edge - SHOULD BE ALIGNED WITH ANCESTOR LIST ITEM */
background: #ccc; /* Styling for clarity */
}
#header ul>li:hover>.submenu{
display: block; /* Shows the submenu */
}
<header id="header">
<nav>
<ul>
<li>
<a>
<h1>
<img>TITLE
</h1>
</a>
</li>
<li>
<a>PAGE</a>
</li>
<li>
<a>PAGE</a>
<ul class="submenu">
<li><a>PAGE</a></li>
<li><a>PAGE</a></li>
<li><a>PAGE</a></li>
</ul>
</li>
<li>
<a>PAGE</a>
</li>
</ul>
</nav>
</header>
Now, to make the positioning do something, I have to select the element to which the positioning will be done. But, if I do it with list element, the submenu will be too far up, because it is positioned under the list element, and the header has some padding.
#header li {
position: relative; /* Positioning will be relative to the ANCESTOR LIST ITEM */
}
#header{
padding: 0.125em;
background: #eee; /* Styling for clarity */
}
nav{
display: inline; /* Makes nav inline */
}
h1{
display: inline; /* Makes the main title inline */
}
#header ul{
padding:0; /* Clears the space around the list */
display: inline; /* Makes lists inline */
}
#header li{
display: inline; /* Makes list items inline */
list-style-type: none; /* Removes bullets */
position: relative; /* Positioning will be relative to the ANCESTOR LIST ITEM */
}
#header ul>li>.submenu>li{
display: block; /* Makes submenu list items behave normally */
}
#header ul>li>.submenu{
display: none; /* Hides the submenu */
position: absolute; /* Enables positioning */
top: 100%; /* Position directly under - SHOULD BE UNDER HEADER */
left: 0; /* Align left edge - SHOULD BE ALIGNED WITH ANCESTOR LIST ITEM */
background: #ccc; /* Styling for clarity */
}
#header ul>li:hover>.submenu{
display: block; /* Shows the submenu */
}
<header id="header">
<nav>
<ul>
<li>
<a>
<h1>
<img>TITLE
</h1>
</a>
</li>
<li>
<a>PAGE</a>
</li>
<li>
<a>PAGE</a>
<ul class="submenu">
<li><a>PAGE</a></li>
<li><a>PAGE</a></li>
<li><a>PAGE</a></li>
</ul>
</li>
<li>
<a>PAGE</a>
</li>
</ul>
</nav>
</header>
This is the result: aligned with list element
And if I do it with the header, the submenu will be aligned with the left of the entire header, which is not what I want.
#header {
position: relative; /* Positioning will be relative to the HEADER */
}
#header{
padding: 0.125em;
background: #eee; /* Styling for clarity */
position: relative; /* Positioning will be relative to the HEADER */
}
nav{
display: inline; /* Makes nav inline */
}
h1{
display: inline; /* Makes the main title inline */
}
#header ul{
padding:0; /* Clears the space around the list */
display: inline; /* Makes lists inline */
}
#header li{
display: inline; /* Makes list items inline */
list-style-type: none; /* Removes bullets */
}
#header ul>li>.submenu>li{
display: block; /* Makes submenu list items behave normally */
}
#header ul>li>.submenu{
display: none; /* Hides the submenu */
position: absolute; /* Enables positioning */
top: 100%; /* Position directly under - SHOULD BE UNDER HEADER */
left: 0; /* Align left edge - SHOULD BE ALIGNED WITH ANCESTOR LIST ITEM */
background: #ccc; /* Styling for clarity */
}
#header ul>li:hover>.submenu{
display: block; /* Shows the submenu */
}
<header id="header">
<nav>
<ul>
<li>
<a>
<h1>
<img>TITLE
</h1>
</a>
</li>
<li>
<a>PAGE</a>
</li>
<li>
<a>PAGE</a>
<ul class="submenu">
<li><a>PAGE</a></li>
<li><a>PAGE</a></li>
<li><a>PAGE</a></li>
</ul>
</li>
<li>
<a>PAGE</a>
</li>
</ul>
</nav>
</header>
This is what it looks like: under header
So, what I want is the "top: 100%;" to be related to the entire header and "left: 0;" to be related to the list element of which the submenu is a child.
I hope my question was clear. I could not find anyone who has asked this question before. I'm sorry I could not put pictures or more links in the question because this is my first question ever, so I do not have enough reputation.
Thanks for reading my question, I hope it will get answered soon!
Upvotes: 0
Views: 281
Reputation: 287990
You can position .submenu
relatively to the header, and then take advantage of left: auto
's behavior.
Then, .submenu
will be an absolutely positioned element whose left
, width
and right
properties are all auto
.
Therefore, according to §10.3.7,
If all three of 'left', 'width', and 'right' are 'auto': First set any 'auto' values for 'margin-left' and 'margin-right' to 0. Then, if the 'direction' property of the element establishing the static-position containing block is 'ltr' set 'left' to the static position and apply rule number three below
That static position is defined as
The static position for 'left' is the distance from the left edge of the containing block to the left margin edge of a hypothetical box that would have been the first box of the element if its 'position' property had been 'static' and 'float' had been 'none'. The value is negative if the hypothetical box is to the left of the containing block.
So let's remove position: absolute
and see:
#header{
padding: 0.125em;
background: #eee; /* Styling for clarity */
position: relative; /* Positioning will be relative to the HEADER */
}
nav{
display: inline; /* Makes nav inline */
}
h1{
display: inline; /* Makes the main title inline */
}
#header ul{
padding:0; /* Clears the space around the list */
display: inline; /* Makes lists inline */
}
#header li{
display: inline; /* Makes list items inline */
list-style-type: none; /* Removes bullets */
}
#header ul>li>.submenu>li{
display: block; /* Makes submenu list items behave normally */
}
#header ul>li>.submenu{
display: none; /* Hides the submenu */
position: static;
top: 100%; /* Position directly under - SHOULD BE UNDER HEADER */
background: #ccc; /* Styling for clarity */
}
#header ul>li>.submenu{
display: block; /* Shows the submenu */
}
<header id="header">
<nav>
<ul>
<li>
<a>
<h1>
<img>TITLE
</h1>
</a>
</li>
<li>
<a>PAGE</a>
</li>
<li>
<a>PAGE</a>
<ul class="submenu">
<li><a>PAGE</a></li>
<li><a>PAGE</a></li>
<li><a>PAGE</a></li>
</ul>
</li>
<li>
<a>PAGE</a>
</li>
</ul>
</nav>
</header>
Effectively, since .submenu
is a block and its parent is inline, submenu
will appear at the beginning of the following line.
To prevent that, .submenu
must generate a block container. Inline-level block containers can be generated with display: inline-block
.
#header{
padding: 0.125em;
background: #eee; /* Styling for clarity */
position: relative; /* Positioning will be relative to the HEADER */
}
nav{
display: inline; /* Makes nav inline */
}
h1{
display: inline; /* Makes the main title inline */
}
#header ul{
padding:0; /* Clears the space around the list */
display: inline; /* Makes lists inline */
}
#header li{
display: inline-block; /* Makes list items inline-block */
list-style-type: none; /* Removes bullets */
}
#header ul>li>.submenu>li{
display: block; /* Makes submenu list items behave normally */
}
#header ul>li>.submenu{
display: none; /* Hides the submenu */
position: static;
top: 100%; /* Position directly under - SHOULD BE UNDER HEADER */
background: #ccc; /* Styling for clarity */
}
#header ul>li>.submenu{
display: block; /* Shows the submenu */
}
<header id="header">
<nav>
<ul>
<li>
<a>
<h1>
<img>TITLE
</h1>
</a>
</li>
<li>
<a>PAGE</a>
</li>
<li>
<a>PAGE</a>
<ul class="submenu">
<li><a>PAGE</a></li>
<li><a>PAGE</a></li>
<li><a>PAGE</a></li>
</ul>
</li>
<li>
<a>PAGE</a>
</li>
</ul>
</nav>
</header>
Now the .submenu
has the desired static position for left.
Therefore, if adding position: absolute
again, it works as desired:
#header{
padding: 0.125em;
background: #eee; /* Styling for clarity */
position: relative; /* Positioning will be relative to the HEADER */
}
nav{
display: inline; /* Makes nav inline */
}
h1{
display: inline; /* Makes the main title inline */
}
#header ul{
padding:0; /* Clears the space around the list */
display: inline; /* Makes lists inline */
}
#header li{
display: inline-block; /* Makes list items inline-block */
list-style-type: none; /* Removes bullets */
}
#header ul>li>.submenu>li{
display: block; /* Makes submenu list items behave normally */
}
#header ul>li>.submenu{
display: none; /* Hides the submenu */
position: absolute;
top: 100%; /* Position directly under - SHOULD BE UNDER HEADER */
background: #ccc; /* Styling for clarity */
}
#header ul>li>.submenu{
display: block; /* Shows the submenu */
}
<header id="header">
<nav>
<ul>
<li>
<a>
<h1>
<img>TITLE
</h1>
</a>
</li>
<li>
<a>PAGE</a>
</li>
<li>
<a>PAGE</a>
<ul class="submenu">
<li><a>PAGE</a></li>
<li><a>PAGE</a></li>
<li><a>PAGE</a></li>
</ul>
</li>
<li>
<a>PAGE</a>
</li>
</ul>
</nav>
</header>
To summarize, the fix is
#header li {
display: inline-block;
}
#header ul > li > .submenu {
left: auto; /* Initial value */
}
Upvotes: 0
Reputation: 2309
Here's the fiddle: http://jsfiddle.net/td6Las4x/
Just add "position:relative" only for the "#header", remove "left:0" for "#header ul > li > .submenu" and "display: inline-block;" for "#header li"
#header li {
display: inline-block;
}
Upvotes: 1