How to make an element sticky until all it's inner content is completely scrolled?

46 views Asked by At

I have built a mobile device in html and want it to get sticky once the window scrolls to this section until all of it's inner content is scrolled past. The problem is that current solution is indeed helping me achieve the inner scroll while preventing main scroll (making it look like sticky) but once the inner content is completely scrolled, instead of window going back to normal scroll behavior, it either scrolls back to top immediately or the scroll becomes laggy, please see the code below and know what i mean.

https://jsfiddle.net/nomnwz/1cLj290y/1/

it's code is here

index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>HTML Device</title>
        <link rel="stylesheet" href="styles.css" />
    </head>
    <body>
        <div class="section-full">1</div>
        <div class="section-full">2</div>
        <div class="section-full">3</div>
        <div class="section-full">4</div>
        <div class="err-mobile">
            <div class="err-device">
                <div class="err-ring">
                    <div class="err-mic"></div>
                    <div class="err-view">
                        <div class="err-cards">
                            <div class="err-card">
                                <div class="err-head">Communication<br><span class="err-secondary">Gaps</span></div>
                                <div class="err-body">
                                    <span class="err-icon no"></span>
                                    <p>Misunderstandings, and lack of clarity. Facing difficulty in locating effective questioners.</p>
                                </div>
                            </div>
                            <div class="err-card">
                                <div class="err-head"><span class="err-secondary">High</span><br>Costs</div>
                                <div class="err-body">
                                    <span class="err-icon no"></span>
                                    <p>Unable to find a vendor that meets budget or offers reasonable pricing.</p>
                                </div>
                            </div>
                            <div class="err-card">
                                <div class="err-head"><span class="err-secondary">Exhausting</span><br>Interviews</div>
                                <div class="err-body">
                                    <span class="err-icon no"></span>
                                    <p>Unable to find or retain all-star talent. Drainer of arranging extensive evaluations inhouse.</p>
                                </div>
                            </div>
                            <div class="err-card">
                                <div class="err-head">Identifying<br><span class="err-secondary">Challenges</span></div>
                                <div class="err-body">
                                    <span class="err-icon warning"></span>
                                    <p>Need assistance identifying and addressing challenges during the entire business development process.</p>
                                </div>
                            </div>
                            <div class="err-card">
                                <div class="err-head">Timeline<br><span class="err-secondary">Constraints</span></div>
                                <div class="err-body">
                                    <span class="err-icon no"></span>
                                    <p>Unable to locate a service provider who consistently meets specified timeline constraints for delivery.</p>
                                </div>
                            </div>
                            <div class="err-card">
                                <div class="err-head"><span class="err-secondary">Quality</span><br>Issues</div>
                                <div class="err-body">
                                    <span class="err-icon no"></span>
                                    <p>Defects in produce services, or processes resulting in unsatisfactory outcomes.</p>
                                </div>
                            </div>
                            <div class="err-card">
                                <div class="err-head"><img src="https://example.com/wp-content/uploads/2023/11/Logo.svg" alt="Logo"></div>
                                <div class="err-body">
                                    <span class="err-icon yes"></span>
                                    <p>Beyond Solutions, Building Partnerships</p>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="err-navigator"></div>
                    <div class="err-power"></div>
                    <div class="err-volume">
                        <div class="err-ringer"></div>
                        <div class="err-up"></div>
                        <div class="err-down"></div>
                    </div>
                </div>
            </div>
        </div>
        <div class="section-full">-2</div>
        <div class="section-full">-1</div>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
        <script src="script.js"></script>
    </body>
</html>

styles.css

* {
    box-sizing: border-box;
}

body {
    font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
    padding: 0;
    margin: 0;
}

.section-full {
    height: 100vh;
}

.err-secondary {
    color: #e37373;
}

.err-mobile {
    font-family: "NeuzeitGro-Bol", Sans-serif;
    display: block;
    width: 100%;
    height: calc(100vh - 10px);
    margin-top: 5px;
    overflow: hidden;
}

.err-mobile .err-device {
    max-width: 340px;
    height: 100%;
    margin: 0 auto;
    border: 3px solid #dcd2d2;
    border-radius: 40px;
    position: relative;
}

.err-mobile .err-device .err-ring {
    background-image: url(https://example.com/wp-content/uploads/2023/12/iphone-wallpaper-scaled.jpg);
    background-size: cover;
    background-repeat: no-repeat;
    width: 100%;
    height: 100%;
    border: 6px solid #000;
    border-radius: 40px;
}

.err-mobile .err-device .err-mic {
    max-width: 100px;
    height: 30px;
    border-radius: 30px;
    background-color: #000;
    margin: 5px auto;
    position: relative;
    z-index: 1;
}

.err-mobile .err-device .err-mic::after {
    content: "";
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background-color: #121212;
    position: absolute;
    top: 5px;
    right: 5px;
}

.err-mobile .err-device .err-view {
    max-width: 100%;
    height: 100%;
    overflow: hidden;
    overflow-y: auto;
    margin-top: -40px;
    border-radius: 40px;
    -ms-overflow-style: none;
    scrollbar-width: none;
}

.err-mobile .err-device .err-view::-webkit-scrollbar {
    display: none;
}

.err-mobile .err-device .err-view .err-cards {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 10px;
    padding-top: 60px;
    padding-bottom: 20px;
}

.err-mobile .err-device .err-view .err-cards .err-card {
    background: rgb(255 247 249 / 94%);
    padding: 20px;
    border-radius: 30px;
    box-shadow: 0 0 20px 0 rgb(255 240 241);
    position: relative;
}

.err-mobile .err-device .err-view .err-cards .err-card .err-head {
    font-family: "Rafiloma", Sans-serif;
    font-size: 28px;
    font-weight: 600;
    position: relative;
}

.err-mobile .err-device .err-view .err-cards .err-card .err-head img {
    max-height: 40px;
    width: auto;
}

.err-mobile .err-device .err-view .err-cards .err-card .err-head::after {
    content: "";
    background-image: url(https://example.com/wp-content/uploads/2023/12/Close-Icon.png);
    background-repeat: no-repeat;
    background-size: cover;
    background-position: center;
    width: 16px;
    height: 16px;
    position: absolute;
    right: 0px;
    top: 25px;
}

.err-mobile .err-device .err-view .err-cards .err-card:last-child .err-head::after {
    background-image: url(https://example.com/wp-content/uploads/2023/11/success-icon.png);
    width: 20px;
    height: 20px;
    top: 10px;
}

.err-mobile .err-device .err-view .err-cards .err-card .err-body {
    font-size: 16px;
    font-weight: 500;
    display: flex;
    gap: 10px;
    align-items: center;
}

.err-mobile .err-device .err-view .err-cards .err-card .err-body .err-icon.no::before {
    content: "";
    background-image: url(https://example.com/wp-content/uploads/2023/11/Error-Icon.png);
    background-repeat: no-repeat;
    background-size: cover;
    background-position: center;
    width: 50px;
    height: 50px;
    display: block;
}

.err-mobile .err-device .err-view .err-cards .err-card .err-body .err-icon.warning::before {
    content: "";
    background-image: url(https://example.com/wp-content/uploads/2023/11/warning-icon.png);
    background-repeat: no-repeat;
    background-size: cover;
    background-position: center;
    width: 50px;
    height: 44px;
    display: block;
}

.err-mobile .err-device .err-view .err-cards .err-card .err-body .err-icon.yes::before {
    content: "";
    background-image: url(https://example.com/wp-content/uploads/2023/11/success-icon.png);
    background-repeat: no-repeat;
    background-size: cover;
    background-position: center;
    width: 50px;
    height: 50px;
    display: block;
}

.err-mobile .err-device .err-navigator {
    max-width: 160px;
    height: 5px;
    border-radius: 5px;
    background-color: #ffffff;
    margin: 5px auto;
    margin-top: -10px;
    z-index: 1;
}

.err-mobile .err-device .err-power {
    content: "";
    width: 5px;
    height: 80px;
    position: absolute;
    top: 40%;
    right: -5px;
    background-color: #dcd2d2;
    border-radius: 5px;
}

.err-mobile .err-device .err-volume .err-ringer,
.err-mobile .err-device .err-volume .err-up,
.err-mobile .err-device .err-volume .err-down {
    content: "";
    width: 5px;
    height: 50px;
    position: absolute;
    top: 30%;
    left: -5px;
    background-color: #dcd2d2;
    border-radius: 5px;
}

.err-mobile .err-device .err-volume .err-ringer {
    height: 30px;
}

.err-mobile .err-device .err-volume .err-up {
    top: calc(30% + 55px);
}

.err-mobile .err-device .err-volume .err-down {
    top: calc(30% + 120px);
}

script.js

jQuery(document).ready(function ($) {
    var mobileDevice = $(".err-mobile");
    var mobileDeviceTop = mobileDevice.offset().top;
    var mobileView = $(".err-mobile .err-device .err-view");

    $(window).scroll(function () {
        var scrollPosition = $(window).scrollTop();

        if (scrollPosition >= mobileDeviceTop - 5) {
            var maxScroll =
                mobileView.prop("scrollHeight") - mobileView.height();

            $("body").css("overflow", "hidden");

            $("body, .err-mobile .err-device .err-view").on(
                "wheel",
                function (e) {
                    if (
                        (e.originalEvent.deltaY < 0 &&
                            mobileView.scrollTop() > 0) ||
                        (e.originalEvent.deltaY > 0 &&
                            mobileView.scrollTop() < maxScroll)
                    ) {
                        mobileView.scrollTop(
                            mobileView.scrollTop() + e.originalEvent.deltaY
                        );
                    } else {
                        $("body").css("overflow", "");
                    }
                }
            );
        } else {
            $("body").css("overflow", "");
        }
    });
});
1

There are 1 answers

0
async await On

One option you have to achieve this desired effect is to build your frame, wrap it in a sticky element with no height but allowed overflow, and have the following element positioned relative with a higher z-index so it can hide the overflow.

Here is a small demo using pure html / css. I made the background a linear gradient so you could see it scroll more clearly with the sticky number heads.

body {
  margin: 0;
  color: hotpink;
  font-weight: bold;
  font-size: 20pt;
  font-family: monospace;
}

section {
  min-height: 200dvh;
  text-align: center;
}

section {
  background:
    linear-gradient(
      #888,
      #000,
      #888
    );
}

section:nth-of-type(3) {
  position: relative;
  z-index: 2;
}

.sticky {
  position: sticky;
  top: 0;
}

.frame-wrap {
  height: 0;
}

.frame {
  box-sizing: border-box;
  height: 100dvh;
  width: 100%;
  border: 15px solid hotpink;
  border-radius: 20px;
}
<section>
  <div class="sticky">1</div>
</section>
<section>
  <div class="sticky frame-wrap">
    <div class="frame"></div>
  </div>
  Enjoy this content <br />
  Enjoy this content <br />
  Enjoy this content <br />
  Enjoy this content <br />
  Enjoy this content <br />
  Enjoy this content <br />
  Enjoy this content <br />
  Enjoy this content <br />
  Enjoy this content <br />
  Enjoy this content <br />
  Enjoy this content <br />
  Enjoy this content <br />
  Enjoy this content <br />
  Enjoy this content <br />
  Enjoy this content <br />
  Enjoy this content <br />
  Enjoy this content
</section>
<section>
  <div class="sticky">3</div>
</section>