Reputation: 15199
So I have two (or potentially more) elements that I want to occupy the same space. They need to fit inside a container element whose size should be automatically made large enough to contain them. I currently have a couple of possibilities that don't quite work, shown below:
.first {
display: inline-block;
position: relative;
border: 1px solid black;
padding: 1px;
}
.first > li {
display: block;
padding: 0px;
margin: 0px;
position: relative;
}
.second {
display: inline-block;
position: relative;
border: 1px solid black;
padding: 1px;
}
.second > li {
display: block;
padding: 0px;
margin: 0px;
position: absolute;
}
.third {
display: inline-block;
border: 1px solid black;
padding: 1px;
}
.third > li {
padding: 0px;
margin: 0px;
float: left;
margin-right: -100%;
}
<div style="float:right; width: 75%">(Attempt 1: note that the two items are not superimposed, but the container is large enough to hold them)</div>
<ul class="first">
<li>Item number 1</li>
<li>Item number 2</li>
</ul>
<br><br>
<div style="float:right; width: 75%">(Attempt 2: note that this time they are superimposed, but no space is allocated in the container for them)</div>
<ul class="second">
<li>Item number 1</li>
<li>Item number 2</li>
</ul>
<br><br><br><br>
Edited to add a third attempt:<br>
<ul class="third">
<li>Item number 1</li>
<li>Item number 2</li>
</ul>
Ideally I'm looking for a pure CSS solution, but it only needs to work on webkit-based browsers (i.e. chrome / safari).
Updated: add a third attempt that gets the two items overlapping, but allocates enough space to hold both of them side by side, which still doesn't really get me where I want to be.
For reference: I don't know the sizes of the items in advance, so can't (for example) size the container to hold the largest and make the rest overlap it.
Upvotes: 1
Views: 3630
Reputation: 4312
absolute
positioning:Setting position: absolute
on all <li>
elements, then setting position: relative
on the .current
(there are many ways to select a dominant <li>
and using a class is just one of them) <li>
gives what I think you're after.
We can then set the visibility
of every <li>
to hidden
, and set the visibility
of .current
to visible
to reduce the visual clutter:
ul {
display: inline-block;
padding: .2em .3em;
list-style: none;
border: 2px solid black;
}
li {
position: absolute;
visibility: hidden;
}
li.current {
position: relative;
visibility: visible;
}
<ul>
<li>a</li>
<li>abc</li>
<li>abcdefg</li>
<li>foo bar baz</li>
<li>i dunno lol</li>
<li class="current">42</li>
</ul>
This approach is effectively identical to using display
none
and list-item
:
ul {
display: inline-block;
padding: .2em .3em;
list-style: none;
border: 2px solid black;
}
li {
display: none;
}
li.current {
display: list-item;
}
<ul>
<li>a</li>
<li>abc</li>
<li>abcdefg</li>
<li>foo bar baz</li>
<li>i dunno lol</li>
<li class="current">42</li>
</ul>
Setting the height
of every <li>
to 0
and giving just the .current
its initial
height
back to push the <ul>
's border
out, we get something like this:
ul {
display: inline-block;
padding: .2em .3em;
list-style: none;
border: 2px solid black;
}
li {
height: 0;
visibility: hidden;
}
li.current {
height: initial;
visibility: visible;
}
<ul>
<li>a</li>
<li>abc</li>
<li>abcdefg</li>
<li>foo bar baz</li>
<li>i dunno lol</li>
<li class="current">42</li>
</ul>
Although the question calls for a CSS solution, where that might be impossible or unreliable, we can always rely on JS.
In the case that both the width
and height
of the child <li>
s may be different, and that the container should be both wide and high enough for every child, JS provides.
The method below uses getComputedStyle
to get the width
and height
of the relative
ly rendered <li>
elements, and sets position: absolute
on them all as they're read.
The parent <ul>
then has its width
and height
set to the largest width
and height
s of the child <li>
s.
The result is a <ul>
that's wide and high enough to contain every child <li>
, but with all the children position
ed absolute
ly;
var widths = [], heights = [];
// loop through all the li elements
document.querySelectorAll( "li" ).forEach( function( v ) {
// get the computed styles for each li element
var ecs = window.getComputedStyle( v );
// push the width into the widths array
widths.push( ecs.width );
// push the height into the heights array
heights.push( ecs.height );
// set position:absolute on this li element
v.style.position = "absolute";
} );
document.querySelector( "ul" ).setAttribute( "style",
/* sort the widths array and pop the last (and therefore largest) value
then apply it to the width of the ul. */
"width:" + widths.sort().pop() +
/* sort the heights array and pop the last (and therefore largest) value
then apply it to the height of the ul. */
";height:" + heights.sort().pop()
);
ul {
display: inline-block;
padding: .2em .3em;
list-style: none;
border: 2px solid black;
}
li {
visibility: hidden;
}
li.current {
visibility: visible;
}
<ul>
<li>a</li>
<li>abc</li>
<li>abcdefg<br>1234567</li>
<li>foo bar baz</li>
<li>i dunno lol</li>
<li class="current">42</li>
</ul>
display: flex
and visibility: hidden
I was wondering if visibility: collapse
might be useful, and found a CSS-tricks.com article linking to a csswg.org draft suggesting that visiblity: collapse
may be used on <li>
s with display: flex
to acheive pretty much exactly what you're after.
I couldn't get it to work, and noticed that their own example was not using either display: flex
or visibility: collapse
!
They are instead manipulating the height
s of the children.
Since it is a draft, this may become a useable solution down the road.
I am unfamiliar with the use of flexboxes (deep shame) so wouldn't be surprised if a solution using them exists. I just don't (at this time) know it.
Upvotes: 0
Reputation: 259
First, you should include your two elements inside a container div that you want to have and write property as:
position: absolute;
top: 0;
left: 0;
And, make your container position property as:
position: relative
Upvotes: 0
Reputation: 433
Why not do it like this:
.second > li {
display: inline-block;
padding: 0px;
margin: 0px;
position: absolute;
border-top: 1px solid black;
border-bottom: 1px solid black;
padding: 1px;
left:0;
}
.second > li:first-child {
border-left: 1px solid black;
}
.second > li:last-child {
border-right: 1px solid black;
}
<ul class="second">
<li>Item number 1</li>
<li>Item number 2 greater length</li>
</ul>
Upvotes: 0
Reputation: 813
like this?
ul {
display: inline-block;
position: relative;
border: 1px solid black;
padding:0; margin:0;
}
ul li {
display: block;
position: absolute;
top:0; left:0;
}
ul li:nth-child(1) {
position:relative;
}
<ul>
<li>Item number 1</li>
<li>Item number 2</li>
</ul>
Upvotes: 2