Reputation: 155872
I have a CSS slide down menu that can vary in the number of elements displayed.
I use an overflow: hidden
wrapper and margin-top: -100%
to hide the menu, and a CSS transition with margin-top: 0
to animate the slide down.
The problem appears to be that margin-top: -100%
seems to be picking up the explicit width
of any parent element and ignoring the content height.
.outer {
overflow: hidden;
border: 10px solid red;
cursor: pointer;
}
.outer > .slide {
margin-top: -100%;
transition-property: margin-top;
transition-duration: 1s;
}
.outer:hover > .slide {
margin-top: 0;
}
.outer.bug {
width: 100px;
}
<div class="outer">
<div class="slide">
<ul>
<li>
Ayy
</li>
<li>
Bee
</li>
<li>
See
</li>
<li>
Dee
</li>
<li>
Eee
</li>
<li>
Eff
</li>
<li>
Gee
</li>
</ul>
</div>
</div>
<div class="outer bug">
<div class="slide">
<ul>
<li>
Ayy
</li>
<li>
Bee
</li>
<li>
See
</li>
<li>
Dee
</li>
<li>
Eee
</li>
<li>
Eff
</li>
<li>
Gee
</li>
</ul>
</div>
</div>
Run that snippet and you should see two red boxes, mouse over either to get the slide down menu.
The problem is the second box - the only difference is that the parent has width: 100px;
set.
If you inspect the second box you should see that the margin-top: -100%;
has been calculated as -100px.
In fact any explicit or inherited width against the parent div will become the negative margin.
The only way I've found round it is to explicitly set the margin-top
to be the same as the height of the sliding menu, but that involves DOM layout and JS that I don't really want to do for each menu.
Is this a bug in how browsers handle negative percentages on margin-top? IE11, Chrome and FX all seem to treat it the same way.
Is there a better way around it that doesn't require an explicit height calculation and additional Javascript?
Upvotes: 2
Views: 3244
Reputation: 8795
margin-top:-100%
works only when we remove border of 20px
assigned .outer
which is used twice 10px + 10px
. So we can make use of CSS calc() function over-here to get our output.
ul
has a default padding-top:40px
so this too is used twice 40px + 40px
, which we have to set to 0px
.
So ul and border is the reason for not working of margin-top:-100%;
Default ul properties :
ul{
display: block;
list-style-type: disc;
margin-before: 1em;
margin-after: 1em;
margin-start: 0;
margin-end: 0;
padding-start: 40px;
}
.outer {
overflow: hidden;
border: 10px solid red;
cursor: pointer;
width:auto;
height:auto;
}
.outer > .slide {
margin-top: calc(-26px + (-100%));
transition-property: margin-top;
transition-duration: 1s;
background:#ccc;
}
.outer > .slide > ul{
padding:0;
margin:0;
}
.outer:hover > .slide {
margin-top: 0;
}
.outer.bug {
width: 100px;
}
<div class="outer">
<div class="slide">
<ul>
<li>Ayy</li>
<li>Bee</li>
<li>See</li>
<li>Dee</li>
<li>Eee</li>
<li>Eff</li>
<li>Gee</li>
</ul>
</div>
</div>
<div class="outer bug">
<div class="slide">
<ul>
<li>Ayy</li>
<li>Bee</li>
<li>See</li>
<li>Dee</li>
<li>Eee</li>
<li>Eff</li>
<li>Gee</li>
</ul>
</div>
</div>
Upvotes: 1
Reputation: 115342
To move an element (regardless of it's height) in relation to its own dimensions you can use a transform:translate()
instead of margin.
So your requirement to move the element up by its own height would require a translation in the Y-axis of -100%. Like so:
* {
margin: 0;
padding: 0;
}
.outer {
/* overflow: hidden; */
padding: 10px;
background: red;
cursor: pointer;
position: relative;
}
.outer > .slide {
position: absolute;
top: 0;
left: 0;
transform: translateY(-100%);
transition-property: transform;
transition-duration: 1s;
}
.outer:hover > .slide {
transform: translateY(0%);
}
<div class="outer">
<div class="slide">
<ul>
<li>Ayy</li>
<li>Bee</li>
<li>See</li>
<li>Dee</li>
<li>Eee</li>
</ul>
</div>
</div>
Then we can use absolute positioning to remove the child from the document flow.
Upvotes: 1