How to "stop" an element after scrolling to another element

44 Views Asked by At

I am trying to emulate the "Engage. Involve. Connect" widget on this page: https://themeworks.com/. Basically, that widget stays set to the bottom of the page until the user scrolls, where it then parks itself right above the "Experiences shape us." text.

I am currently working on creating a similar widget at https://williamh117.sg-host.com/. I'm struggling finding a way to get the #widget / #unanchored element to "stop" upon scrolling to ".xp", a class that is contained by the "Experiences shape us." element.

Here is my current JavaScript:

window.addEventListener('scroll', function() {
  var widget = document.getElementById('widget');
  var xpElement = document.querySelector('.xp');
  var widgetHeight = widget.offsetHeight;
  var xpPosition = xpElement.getBoundingClientRect().top;
  var windowHeight = window.innerHeight;

  var distance = xpPosition - windowHeight + widgetHeight;

  if (distance <= 0) {
    // If the widget encounters the '.xp' element, un-anchor it and let it scroll with the page content
    widget.classList.add('unanchored');
  } else {
    // If the widget is still anchored, position it fixed at the bottom of the viewport
    widget.classList.remove('unanchored');
  }
  
  // Set the height of .xp element dynamically
  xpElement.style.setProperty('--xp-height', widgetHeight + 'px'); // Set xpHeight to widgetHeight
});

Here is my HTML:

<h3 id="widget" class="wp-block-heading has-text-align-center frontpage ticss-f97cde6c has-white-color has-text-color has-background" style="background-color:#00000087;font-size:39px">Engage. &nbsp; Involve. &nbsp; Connect.<br><span class="text frontpage2" style="font-weight: 200 !important;">We fuse Art + Engineering to create world-class experiences.</span><br><a href="https://williamh117.sg-host.com/#spacestop"><i class="arrow down"></i></a></h3>

Here is my CSS:

.unanchored {
  position: fixed !important; /* Change position to fixed */
  bottom: 300px !important;
  top: calc(100% - var(--widget-height) - 30px); /* Adjust this value to set the desired distance from .xp */
  left: 0 !important;
    margin-bottom:0px !important;
}

.xp {
  --widget-height: 30px; /* Adjust this value to match the height of your widget */
  position: relative; /* Keep position relative */
}

#widget {
  position: fixed;
  bottom: 0;
  left: 0;
  background-color: #f0f0f0;
  width: 100%;
  text-align: center;
  transition: bottom 0.3s ease-in-out;
    z-index:1000 !important;
    scroll-behavior: smooth !important;
}

Thank you in advance for any and all advice.

1

There are 1 best solutions below

0
Tun Lin Phyo On

I guess you want similar function like that. Check at codePin

<!DOCTYPE html>
<html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
    .banner {
        width: 100%;
        height: 100vh; /* Note */
        background-color: #098;
    }
    .widget {
        position: relative; /* Note */
        z-index: 9;
        top: -200px; /* Widget Height */
        width: 100%;
        height: 200px;
        background-color: #0005;

        display: grid;
        place-content: center;
        color: #fff;
    }
    .content {
        position: relative; /* Note */
        margin-top: -200px; /* Widget Height */
        width: 100%;
        height: 1000px;
        background-color: #000;
        padding-top: 200px;
        color: #fff;
    }
    </style>
</head>
<body>
    <section class="banner"></section>
    <section class="widget">
        <h2>Widget Content</h2>
    </section>
    <section class="content">
        <div class="children">
            <h1>This is Title</h1>
        </div>
    </section>

    <script>
        function map_range(value, input1Start, input1End, input2Start, input2End) {
            const normalizedValue = (value - input1Start) / (input1End - input1Start);
            return input2Start + normalizedValue * (input2End - input2Start);
        }
        window.addEventListener('scroll', function() {
            const wantedHeightToEnd = window.innerHeight * 0.5; // want to end animation at middle of the screen
            if (window.scrollY < wantedHeightToEnd) {
                const value = map_range(window.scrollY, 0, wantedHeightToEnd, -200 /* Widget Height */, 0);
                document.querySelector(".widget").style.top = value + px;
            }
        });
    </script>
</body>
</html>

Noted banner, widget and next section are in same level.