Jules
Jules

Reputation: 15199

Use CSS to position two elements at the same location and make container large enough to hold them

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

Answers (4)

Fred Gandt
Fred Gandt

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>

Or for the container to always be wide enough for the longest string:

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>

Using JavaScript :O

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 relatively 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 heights 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 positioned absolutely;

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 heights 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

Suresh Sapkota
Suresh Sapkota

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

Samar Rizvi
Samar Rizvi

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

Joel Glovacki
Joel Glovacki

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

Related Questions