skip to Main Content

A react component has a button that opens or closes a div by changing its height style property. The div also has a transition style property which should presumably animate the change in height. However, what we see instead is an instantaneous change in height with the transition duration acting as a delay.

JSFiddle: https://jsfiddle.net/antshekhter/xh0b8kw3/

Component

const Component = () => {
  const [isOpen, setOpen] = useState(false);

  return (  
    <div>

      <button onClick={() => setOpen(!isOpen)}>Show Content</button>

      <div className="content" style={{ height: isOpen ? "100%" : 0 }}>
        <p>Content</p>
      </div>

    </div>
  );
};

CSS

.content {
  height: 0;
  overflow: hidden;
  transition: height ease 0.5s;
  border-style: solid;
}

I tried using position: fixed and although it works, the result in my real application of this code created a situation where the content passed overtop of some elements below. I want it such that the elements below get "pushed down" as the content expands to its max height.

3

Answers


  1. The problem here is you are using 100% for the height. When using CSS transitions, the element’s height needs to be explicitly defined for the transition to take effect. However, animating the height property from 0 to 100% is not straightforward because the initial height value is unknown. You can check it again by changing 100% to 100px

    In your case you need a height of content inside, I suggest create a useRef to read the height of content inside. Something like this

      const contentRef = React.useRef(null);
    
      React.useEffect(() => {
        const contentElement = contentRef.current;
        if (isOpen) {
          contentElement.style.height = `${contentElement.scrollHeight}px`;
        } else {
          contentElement.style.height = "0";
        }
      }, [isOpen]);
    

    Demo

    Login or Signup to reply.
  2. The problem with your transition is that you’re setting the height of the div to 100% when it’s open, which means it will try to fill up the whole space of its parent element. But the parent element doesn’t have a fixed height, so the browser can’t figure out how to smoothly change the height from 0 to 100%. That’s why the div just snaps open and closed instead of sliding.

    A possible fix is to give the div a specific height when it’s open, like 100px or 10em. Then the browser can animate the height from 0 to that value. For example:

    <div className="content" style={{ height: isOpen ? "100px" : 0 }}>
      <p>Content</p>
    </div>
    
    Login or Signup to reply.
  3. I would recommend you not to set height. change the class name. transition act based on css.

    <div>
      <div onClick={() => setIsOpened((prev) => !prev)}>Button</div>
      <div className="content">
         <p className={isOpened ? "active" : ""}>Content</p>
      </div>
    </div>
    
    .content > p {
      height: 0px;
      overflow: hidden;
      border-style: solid;
      transition: height 1s ease-in;
    }
    
    .content > p.active {
      height: 100px;
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search