Search code examples
javascriptanimationhtmlelements

How can I figure out what size an HTML Element will be? (tween size as element added)


I'm pretty sure this is currently infeasable.

I have an animation that involves an element moving from an absolute position to an inline one. For reasons, I can not know how the container is sized, nor how the element I'm animating is sized.

What I need to know is what the size of the HTML Element will be after the transformation, without any jittery drawing.

This makes the problem very difficult (likely undoable) because I have no way to know if adding the element will resize the parent, or resize the element itself.

What I need is a means of looking into the future.

const byId = (id) => document.getElementById(id);
#container {
  height: 3em;
  min-width: 50%;
  background: teal;
 }

#mystery {
  background: purple;
}
<div id="container">
  <div id="mystery">Some Text</div>
</div>


<button onClick='byId("mystery").style.position = "relative"'>Position Relative</button>
<button onClick='byId("mystery").style.position = "absolute"'>Position Absolute</button>


Currently, these are the only solutions I can imagine (they're all absurd):

  1. Clone the entire webpage HTML, make the clone have opacity: 0; pointer-events: none and render what the future will be secretly.

  2. Capture the paint data of the current page (basically screenshot), overlay that while secretly modifying the page, get my future, revert, and remove the screenshot overlay.

  3. Similar to number 2, is there a way to ❄️freeze❄️ rendering of a page for 3-4 frames?

  4. I remember seeing a "sizing worker" something-or-rather a long time ago. Couldn't find any information on it now, but it seems like it might be useful?


Solution

  • You can simply change the property, measure the sizes you want and then change the property back. JS is fast enough to do it all between renderings, as long as you keep it all in the same thread. Have you tried that at all?


    Asker Edit: Here's the code to prove it works.

    function byId(id){ return document.getElementById(id); }
    const tweenyEl = byId("tweeny");
    
    function appendTweeny() {
      tweenyEl.style.opacity = "1";
      const startingWidth = tweenyEl.clientWidth + "px"
      tweenyEl.style.position = "relative";
      const targetWidth = tweenyEl.clientWidth + "px";
      console.log(startingWidth, targetWidth);
      
      tweenyEl.style.width = startingWidth;
      requestAnimationFrame(() => 
        requestAnimationFrame(() =>
          tweenyEl.style.width = targetWidth
        )
      );
    }
    
    function resetTweeny() {
      tweenyEl.style.position = "";
      tweenyEl.style.width = "";
      tweenyEl.style.opacity = "0.1";
    }
    #container {
      display: inline-block;
      height: 3em;
      min-width: 150px;
      background: teal;
    }
    
    #tweeny {
      font-family: arial;
      color: white;
      position: absolute;
      background: purple;
      transition: all 0.5s ease;
      opacity: 0.1;
    }
    <div id="container">
      <div id="tweeny">I'm Tweeny</div>
    </div>
    
    <br>
    <button onClick='appendTweeny()'>Append Tweeny</button>
    <button onClick='resetTweeny()'>Reset Tweeny</button>