javascriptgetclientrect

elem.getBoundingClientRect() broken on mobile screen


I'm developing a website using gatsby.js and it involves a slide-in animation as you scroll down. I wrote code that worked perfectly until I opened dev tools and tried it using the device toolbar. here's a reproduction demo as well as a webpage to make it easier https://getboundingclientrect-is-broken.netlify.app


        <div class="0 space"></div>

        <p class="1 slideFR"></p>
        <div id="boy" class="2 slideFL"></div>

        <p class="3 slideFR"></p>
        <div class="4 slideFL"></div>

        <div class="flx space"></div>

            .slideFR {
                width: 100px;
                height: 100px;
                background-color: #957b26;
                position: relative;
                left: 450px;

                transform: translateX(1000px);
            }
            .slideFL {
                width: 100px;
                height: 100px;
                background-color: #26958f;
                position: relative;
                left: 300px;

                transform: translateX(-1000px);
            }
            .inSight {
                transition: all 0.5s;
                transform: translateX(0);
            }
            .space {
                width: 100px;
                height: 1500px;
                background-color: aquamarine;
            }
let elemsFL = document.getElementsByClassName("slideFL");
        var leftiesLoaded = Array.from( { length: elemsFL.length }, (_, i) => false ); // creates array length of elemsFL full of <false>

        let elemsFR = document.getElementsByClassName("slideFR");
        var rightersLoaded = Array.from( { length: elemsFR.length }, (_, i) => false ); // creates array length of elemsFR full of <false>

        document.addEventListener("scroll", function (event) {
            let windowHeight = window.outerHeight;
            console.log( "%c/* ----------- scroll ---------- */", "color: purple; font-weight: bold" );

            checkIfInSight(elemsFL, leftiesLoaded, windowHeight);
            checkIfInSight(elemsFR, rightersLoaded, windowHeight);
        });
        /* -------------------------------- touchmove ------------------------------- */
        document.addEventListener("touchmove", function (event) {
            let windowHeight = window.outerHeight;
            console.log( "%c/* ---------- touchmove --------- */", "color: red; font-weight: bold" );
            checkIfInSight(elemsFL, leftiesLoaded, windowHeight);
            checkIfInSight(elemsFR, rightersLoaded, windowHeight);
        });

        function checkIfInSight(elemArray, boolArray, windowHeight) {
            for (let counter = 0; counter < elemArray.length; counter++) {
                const elem = elemArray[counter];
                let elemRect = elem.getBoundingClientRect();
                let elemPosTop = elemRect.top;
                let elemPosBottom = elemPosTop + elem.scrollHeight;

                if (elemPosTop <= windowHeight && elemPosBottom >= 0) {
                    if (!boolArray[counter]) {
                        console.log( "%c In Sight", "color: green", elem.classList[0] );
                        boolArray[counter] = true;
                        elem.classList.add("inSight");
                    } else {
                        console.log( "%c In Sight And Loaded", "color: yellow", elem.classList[0] );
                    }
                } else {
                    console.log( elem.classList[0], "\tOut Of Sight", elemPosTop, "<=", windowHeight, "&&", elemPosBottom, ">=0\t\t\t", elem.offsetTop );
                    boolArray[counter] = false;
                    elem.classList.remove("inSight");
                }
            }
        }

Edit: As I'm troubleshooting this I replaced elem.offsetTop with window.scrollY which indeed made me realize that for some reason the it is not interpreting the scroll action as actually scrolling for quite a while. I still don't know what I'm doing wrong or what the issue is


Solution

  • thanks to EmielZuurbier's comment I found the solution IntersectionObserver API was the way to go. I even produced cleaner more optimized code.

    HTML

    
            <div class="0 space"></div>
    
            <p class="1 slideFR toSlide"></p>
            <div id="boy" class="2 slideFL toSlide"></div>
    
            <p class="3 slideFR toSlide"></p>
            <div class="4 slideFL toSlide"></div>
    
            <div class=" space"></div>
    

    JS

    const slideDivs = document.querySelectorAll(".toSlide");
            const options={
                root: null,
                rootMargin: "0px 2000px",
            };
    
            const observer= new IntersectionObserver(function(entries, observer){
                entries.forEach(entry =>{
                    console.log(entry.target.classList[0],entry.isIntersecting, entry.intersectionRect);
                    if (entry.isIntersecting ){
                        entry.target.classList.add("inSight");
                    }else {
                        entry.target.classList.remove("inSight");
                    }
                });
            },options);
            slideDivs.forEach(slideDiv => {
                observer.observe(slideDiv);
            });
    

    CSS

    
                .slideFR {
                    width: 100px;
                    height: 100px;
                    background-color: #957b26;
                    position: relative;
                    left: 200px;
    
                    transform: translateX(1000px);
                }
                .slideFL {
                    width: 100px;
                    height: 100px;
                    background-color: #26958f;
                    position: relative;
                    left: 150px;
    
                    /* visibility: hidden; */
                    transform: translateX(-1000px);
                }
                .inSight {
                    transition: all 0.5s;
                    transform: translateX(0);
                }
                .space {
                    width: 100px;
                    height: 1500px;
                    background-color: aquamarine;
                }