How to hide an element and still be able to transition it and also have something sticky inside it?

Viewed 47

On button press, I am trying to unhide pads_and_pool element by transitioning its height from 0 to pads element's height.

At the same time, I want the pool element to be sticky, hence the position: sticky and top: 0 on it.

However, this will not work due to the overflow: hidden being set on pads_and_pool.

Is there any other way - that would allow for stickiness - of initially hiding the pads_and_pool element and still being able to transition it to desired height upon button press?

Preferably, I would like to avoid changing the overflow to scroll or removing the pool element from the flexbox.

I am looking for either CSS or JS solution.

let fill_btn = document.getElementById('unhide')
fill_btn.addEventListener('click', unhide_pads_and_pool)
                    
function unhide_pads_and_pool() {
  let pads = document.getElementById('pads')
  let pads_and_pool =           document.getElementById('pads_and_pool')  
  pads_and_pool.style.height = pads.offsetHeight + 'px'                
}
#pads_and_pool {
  height: 0;
  overflow: hidden;
  background: grey;
  display: flex;
  justify-content: space-between;
  transition: height 2s;
}
#pads {
  height: 5000px;
  width: 500px;
  background: orange;
}
#pool {
  position: sticky;
  top: 0;
  height: 150px;
  width: 150px;
  background: powderblue;
}
<button id="unhide">Unhide pads and pool</button>
<div id="pads_and_pool">
    <div id="pads">pads</div>
    <div id="pool">pool</div>
</div>

2 Answers

You could listen for the transitionend event and remove overflow: hidden; once the element has finished its height transition:

let fill_btn = document.getElementById('unhide');
fill_btn.addEventListener('click', unhide_pads_and_pool);

function handleTransitionEnd(event) {
  event.target.style.overflow = 'visible';
  event.target.removeEventListener('transitionend', handleTransitionEnd);
}

function unhide_pads_and_pool() {
  let pads = document.getElementById('pads');
  let pads_and_pool = document.getElementById('pads_and_pool');
  pads_and_pool.addEventListener('transitionend', handleTransitionEnd);
  pads_and_pool.style.height = pads.offsetHeight + 'px'                
}
#pads_and_pool {
  height: 0;
  overflow: hidden;
  background: grey;
  display: flex;
  justify-content: space-between;
  transition: height 2s;
}
#pads {
  height: 5000px;
  width: 500px;
  background: orange;
}
#pool {
  position: sticky;
  top: 0;
  height: 150px;
  width: 150px;
  background: powderblue;
}
<button id="unhide">Unhide pads and pool</button>
<div id="pads_and_pool">
    <div id="pads">pads</div>
    <div id="pool">pool</div>
</div>

position sticky will not work if the element has parents (direct/indirect) that has overflow property set other than the default value (visible).

From MDN

This value always creates a new stacking context. Note that a sticky element "sticks" to its nearest ancestor that has a "scrolling mechanism" (created when overflow is hidden, scroll, auto, or overlay), even if that ancestor isn't the nearest actually scrolling ancestor. This effectively inhibits any "sticky" behavior (see the Github issue on W3C CSSWG).

So my proposed solution is to just change the overflow value to the default one which is visible once you unhide the container.

let fill_btn = document.getElementById('unhide')
fill_btn.addEventListener('click', unhide_pads_and_pool)
                    
function unhide_pads_and_pool() {
  let pads = document.getElementById('pads')
  let pads_and_pool =           document.getElementById('pads_and_pool')  
  pads_and_pool.style.height = pads.offsetHeight + 'px';
  pads_and_pool.style.overflow = 'visible';              
}
#pads_and_pool {
  height: 0;
  overflow: hidden;
  background: grey;
  display: flex;
  justify-content: space-between;
  transition: height 2s;
}
#pads {
  height: 5000px;
  width: 500px;
  background: orange;
}
#pool {
  position: sticky;
  top: 0;
  height: 150px;
  width: 150px;
  background: powderblue;
}
<button id="unhide">Unhide pads and pool</button>
<div id="pads_and_pool">
    <div id="pads">pads</div>
    <div>
      <div id="pool">pool</div>
    </div>
</div>

Related