skip to Main Content

I have four HTML elements, and one of them is scrolling downward with a scroll effect. I want the green-colored div to always stay above Block B but below Block C. Additionally, I need Block C to become hidden behind Block B when they overlap. Also, the darker green element should be shown the same way as Block A.

Edit: Corrected Codesandbox.
Codesandbox

return (
      <div className=" h-[200vh] w-full flex flex-col items-center">

        <div className=" relative mt-[300px] bg-green-900 w-full h-[200px] text-black text-center flex flex-col justify-between text-white ">
          <div className=" relative z-10">
            Block A
          </div>
        </div>

        <Image
          src={borderSvgTexture}
          className=" relative  h-auto  w-full min-w-screen  mt-[-5px]   "
          alt=""
        />

        <div className=" bg-blue-600 w-full h-[200px] mt-[-50px]  ">
            <div className=' mt-[100px] text-center'>Block B</div>
        </div>

      <motion.div
        style={{ y }}
        className="h-[500px] flex justify-center absolute inset-x-0 mx-auto max-w-[1800px] text-white"
        >
        <div className=" w-[100px] h-[100px] bg-red-500">Block C</div>
      </motion.div>
    </div>
  )

I’ve tried various z-index combinations but can’t achieve the desired results, something unintended always ends up getting hidden. Now, I am not sure if this even possible.

3

Answers


  1. To achieve the desired behavior with the HTML elements you described, you can follow these steps:

    Place "Block C" inside "Block B" so that it becomes a child of "Block B."

    Set a z-index for "Block B" higher than that of "Block C" to ensure it appears above "Block C" when they overlap.

    Use position: relative for "Block A" to create a positioning context for the z-index.

    Make sure "Block C" has position: absolute so you can control its position relative to its parent, "Block B."

    Here’s how you can implement these changes in your code:

    return (
      <div className="h-[200vh] w-full flex flex-col items-center">
        <div className="relative mt-[300px] bg-green-900 w-full h-[200px] text-black text-center flex flex-col justify-between text-white">
          <div className="relative z-10">Block A</div>
        </div>
    
        <Image
          src={borderSvgTexture}
          className="relative h-auto w-full min-w-screen mt-[-5px]"
          alt=""
        />
    
        <div className="bg-blue-600 w-full h-[200px] mt-[-50px]">
          <div className="mt-[100px] text-center relative z-20">Block B</div>
          <div className="absolute z-10 w-[100px] h-[100px] bg-red-500">Block C</div>
        </div>
      </div>
    );
    

    With these changes, "Block B" will have a higher z-index than "Block C," ensuring it appears above "Block C" when the elements overlap. At the same time, "Block A" retains its position and positioning context, allowing you to manage the stacked elements as desired.

    Login or Signup to reply.
  2. I apologize for the oversight. To make sure "Block C" hides behind "Block B" when they overlap, you can adjust the z-index and positioning of the elements as follows:

    return (
      <div className="h-[200vh] w-full flex flex-col items-center relative">
    
        <div className="relative mt-[300px] bg-green-900 w-full h-[200px] text-black text-center flex flex-col justify-between text-white z-10">
          <div>Block A</div>
        </div>
    
        <Image
          src={borderSvgTexture}
          className="relative h-auto w-full min-w-screen mt-[-5px]"
          alt=""
        />
    
        <div className="bg-blue-600 w-full h-[200px] mt-[-50px] relative z-20">
          <div className='mt-[100px] text-center'>Block B</div>
        </div>
    
        <motion.div
          style={{ y }}
          className="h-[500px] flex justify-center absolute inset-x-0 mx-auto max-w-[1800px] text-white z-30"
        >
          <div className="w-[100px] h-[100px] bg-red-500">Block C</div>
        </motion.div>
      </div>
    );
    

    In this updated code, I’ve added z-10, z-20, and z-30 classes to control the stacking order. "Block A" and "Block B" have lower z-index values (10 and 20, respectively) to ensure that "Block C" (z-index 30) appears above them.

    With these adjustments, "Block C" should now hide behind "Block B" when they overlap.

    Login or Signup to reply.
  3. I have created a solution to this based using the Element:getBoundingClientRect() for identifying component scroll positions and dimensions in react.

    Basically utilizing useRef hooks for getting the component properties, and getting the top and bottom for the divs. We are able to define conditions when the Block C height should shrink, expand, or z-index changed.

    Updated: to include hiding behind the image pattern.

    Take a look at the CodeSandbox, where I have implemented this method on a fork of your code. Note: On the sandbox sometimes randomly Block C is not visible initially.Please refresh when that happens to ensure all 3 blocks are visible, so initialHeight for Block C is properly captured

    The key function is described below, this listenToScroll is called via useEffect whenever the user scrolls as the scrollHandler.

    Essentially it defines three conditions, for the Block C and affects the height and z-index class

    const listenToScroll = () => {
      if (cRef.current && bRef.current) {
        const cRect = cRef.current.getBoundingClientRect();
        const bRect = bRef.current.getBoundingClientRect();
        
        const cBottom = cRect.bottom;
        const cTop = cRect.top;
        const bTop = bRect.top + (bRect.height/5); // To take image height into account
        let cHeight = cInitialHeight;
    
        if (cBottom > bTop && cTop < bTop){ //Case when scrolling down and C starts to overlap B
            cHeight = cRect.height - (cBottom - bTop);
            cHeight = cHeight < 0 ? 0 : cHeight;
            cHeight = cHeight + "px";
            setCStyle({...cStyle, height: cHeight})
            setCIndex("z-30");
        }
        else if (cBottom < bTop && cRect.height < cInitialHeight){ // Case when scrolling up and C starts going out and above B 
            cHeight = cRect.height + (bTop - cBottom);
            cHeight = cHeight > cInitialHeight ? cInitialHeight : cHeight;
            cHeight = cHeight + "px";
            setCStyle({...cStyle, height: cHeight})
            setCIndex("z-30");
          }
        else if(cTop > bTop){ // Case when completely inside block B
          cHeight = cInitialHeight;
          setCStyle({...cStyle, height: cHeight});
          setCIndex("z-0");
        }
      }
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search