Ibtisam Bhatti
Ibtisam Bhatti

Reputation: 1

Horizontal scrolling issue with button click

I want to replicate a section for my site. The inspiration I took is from clay.com and I want to replicate the How it works section. So far my code is having two main issues, the button functionality is not working properly, and second, after refreshing the page when I scroll the overall section gets disturbed. The code is custom and will be implemented on Elementor. My code is:

<!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>
    

    body {
        margin: 0;
        background: #15151e;
        overflow-x: hidden;
    }

    .racesWrapper {
        position: relative;
        height: 100vh; / Adjust based on the number of slides /
        overflow: hidden;
        transition: background-color 1s ease; / Add this line for smooth color transition /
    }

    .swiper-container {
        height: 100%;
    }

    .swiper-wrapper {
        display: flex;
        flex-direction: row;
    }

    .swiper-slide {
        font-family: 'Staatliches', cursive;
        /*font-size: 30vw;*/
        /*color: #e10600;*/
        display: flex;
        align-items: center;
        justify-content: center;
        text-align: center;
        height: 80vh;
        width: 100vw; / Full width for horizontal scroll /
        /*background: #e1e1ff;*/
        /*background: #e1e1ff36;*/
    }

    .button-container {
        position: absolute;
        top: 10px;
        z-index: 1000;
        display: flex;
        justify-content: space-around;
        width: 100%;
    }

    .button-container button {
        display: block;
        margin-bottom: 5px;
        width: 24%;
        height: 50px;
    }

    .progress-bar {
        position: absolute;
        bottom: 0;
        left: 0;
        height: 5px;
        background: #333;
        width: 100%;
        z-index: 1000;
    }

    .progress-bar-inner {
        height: 100%;
        background: #e10600;
        width: 0;
        transition: width 0.2s ease, background-color 0.2s ease;
    }
    
    /*Content styling start*/
    .swiper-slide .row {
        /*border: 1px solid red;*/
        width: 100%;
        height: 100%;
        margin: 0 auto;
    }
    .swiper-slide .col-md-6 {
        /*border: 1px solid blue;*/
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: flex-start;
        padding-inline: 50px;
    }
    .swiper-slide .col-md-6 :is(p,h2){
        text-align: left;
    }
    .swiper-slide .img-wrapper {
        width: 75%;
    }
    .swiper-slide .img-col{
        align-items: center !important;
        justify-content: center !important;
    }
    
    .swiper-slide a.cta-btn {
        background: #222;
        color: white;
        padding: 10px 20px;
        border-radius: 5px;
    }
</style>
</head>
<body>
    <div class="racesWrapper">
        <!-- Swiper -->
        <div class="swiper-container">
            <div class="swiper-wrapper">
                <div class="swiper-slide">
                    <div class="row">
                        <div class="col-md-6">
                        <h2>Unbeatable Performance 14X Faster Speed</h2>
                        <p>Devrims provides enterprise-grade, managed cloud servers tailored to your specific website and project requirements. The platform offers high performance storage, lightweight technology stacks optimized for PHP applications.</p>
                        <a class="cta-btn">Start Free Trial</a>
                        </div>
                        
                        <div class="col-md-6 img-col">
                        <div class="img-wrapper">
                            <img src="https://images.unsplash.com/photo-1551288049-bebda4e38f71?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D">
                        </div>
                        </div>
                    </div>
                </div>
                <div class="swiper-slide">Flexibility</div>
                <div class="swiper-slide">Reliability</div>
                <div class="swiper-slide">Costing</div>
            </div>
        </div>
        <div class="progress-bar">
            <div class="progress-bar-inner"></div>
        </div>
    
        <div class="button-container">
            <button onclick="goToSlide(0)">Performance</button>
            <button onclick="goToSlide(1)">Flexibility</button>
            <button onclick="goToSlide(2)">Reliability</button>
            <button onclick="goToSlide(3)">Costing</button>
        </div>
    </div>

    <script src="https://unpkg.com/swiper/swiper-bundle.min.js" defer></script>
<script src="https://unpkg.com/gsap@3/dist/gsap.min.js" defer></script>
<script src="https://unpkg.com/gsap@3/dist/ScrollTrigger.min.js" defer></script>
<script src="https://unpkg.com/gsap@3/dist/ScrollToPlugin.min.js" defer></script>
<script>
gsap.registerPlugin(ScrollTrigger, ScrollToPlugin);

    const swiper = new Swiper('.swiper-container', {
        direction: 'horizontal',
        loop: false,
        speed: 1000,
        slidesPerView: 1,
        spaceBetween: 0,
    });

    function getSwiperWrapperWidth() {
        return document.querySelector(".swiper-wrapper").scrollWidth;
    }

    const horizontalScroll = gsap.to(".swiper-wrapper", {
        xPercent: -100 * (swiper.slides.length - 1),
        ease: "none",
        scrollTrigger: {
            trigger: ".racesWrapper",
            pin: true,
            scrub: 1,
            markers: true,
            start: 'top top',
            end: () => `+=${getSwiperWrapperWidth()}`,
            invalidateOnRefresh: true,
            onUpdate: self => {
                const progress = self.progress;
                document.querySelector(".progress-bar-inner").style.width = `${progress * 100}%`;

                const colorIndex = Math.floor(progress * (colors.length - 1));
                document.querySelector(".racesWrapper").style.backgroundColor = colors[colorIndex];
                gsap.to(".racesWrapper", {
                backgroundColor: colors[colorIndex],
                duration: 0.05, // Adjust duration if necessary
                ease: "power1.inOut" // Smooth easing function
            });
            }
        }
    });

    const colors = [
        "#DADDEF", "#D6DAEE", "#D2D5EC", "#CDD3EB", "#CACFE9",
        "#C6CAE7", "#C1C7E5", "#BDC3E3", "#B9C0E1", "#ADB5DC",
        "#A9B1DA", "#A5AED8", "#A2ABD6", "#9DA6D4", "#98A3D2",
        "#919CCF", "#9EA4D3", "#A7AAD5", "#B1AEC8", "#BCB2B6",
        "#C8B7A3", "#D3BB91", "#DFC07D", "#EAC36A", "#F6C85A",
        "#FFCC4A", "#F3C264", "#E9B780", "#DDAC9C", "#D2A1B9",
        "#C697D2", "#BB8BEF", "#B384FE", "#B384FE", "#B384FE",
        "#B384FE", "#B384FE", "#B384FE"
    ];

    function updateProgressBar() {
        const progress = (swiper.activeIndex / (swiper.slides.length - 1)) * 100;
        document.querySelector(".progress-bar-inner").style.width = `${progress}%`;

        const colorIndex = Math.floor((swiper.activeIndex / (swiper.slides.length - 1)) * (colors.length - 1));
        document.querySelector(".racesWrapper").style.backgroundColor = colors[colorIndex];
        gsap.to(".racesWrapper", {
                backgroundColor: colors[colorIndex],
                duration: 0.05, // Adjust duration if necessary
                ease: "power1.inOut" // Smooth easing function
            });
    }

    function goToSlide(index) {
    // Slide to the desired index
    swiper.slideTo(index);

    // Define the swiper wrapper width
    const swiperWrapperWidth = 5616;

    // Determine the scroll position based on the button clicked
    let scrollPosition;
    switch (index) {
        case 0:
            // Fixed scroll position for the first button
            scrollPosition = 1400;
            break;
        case 1:
            // 26% of the swiper wrapper width
            // scrollPosition = swiperWrapperWidth * 0.26;
            scrollPosition = 3108;
            break;
        case 2:
            // 52% of the swiper wrapper width
            // scrollPosition = swiperWrapperWidth * 0.52;
            scrollPosition = 4812;
            break;
        case 3:
            // 76% of the swiper wrapper width
            // scrollPosition = swiperWrapperWidth * 0.76;
            scrollPosition = 6520;
            break;
        default:
            // Default to the calculated position for other slides
            scrollPosition = (index / (swiper.slides.length - 1)) * swiperWrapperWidth;
            break;
    }

    // Log the scroll position for debugging
    console.log("Scroll Position: ", scrollPosition);
    
    // Smoothly scroll to the calculated position
    gsap.to(window, {
        scrollTo: { 
            y: scrollPosition, 
        autoKill: false },
        duration: 0.5,
        ease: "linear"
    });

    // Update the progress bar and background color
    updateProgressBar();
    
    // Refresh ScrollTrigger to account for changes
    ScrollTrigger.refresh();
}




    swiper.on('slideChange', () => {
        updateProgressBar();
    });

    // Initial update
    updateProgressBar();
    
</script>
</body>
</html>

I tried the code mentioned above but the button functionality is not working properly

Upvotes: 0

Views: 119

Answers (1)

Richard
Richard

Reputation: 489

After testing your code, I found some issues which leads to some errors in the console.

  • error "Cannot access swiper before initialization": This is because the swiper variable is being accessed before it is initialized.
  • error "goToSlide is not defined": The goToSlide function is not defined in a scope accessible to the button onclick handlers.

Fixes:

Used DomContentLoaded event ensuring proper initialization of the swiper variable inside a DOMContentLoaded event listener. This ensures the DOM is fully loaded before the script runs, so swiper is initialized before any function tries to access it. This is done by wrapping your initial code inside the DOMContentLoaded event listener.

document.addEventListener('DOMContentLoaded', function() {
    const swiper = new Swiper('.swiper-container', {
        direction: 'horizontal',
        loop: false,
        speed: 1000,
        slidesPerView: 1,
        spaceBetween: 0,
    });
});

Defining goToSlide function in the correct scope, within the DOMContentLoaded event listener. Assign it to window object to make it globally accessible.

function goToSlide(index) { 
    swiper.slideTo(index);
       // Additional logic... 
} 
window.goToSlide = goToSlide;

The full correct code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Swiper and GSAP Integration</title>
    <link rel="stylesheet" href="https://unpkg.com/swiper/swiper-bundle.min.css">
    <style>
        body {
            margin: 0;
            background: #15151e;
            overflow-x: hidden;
        }

        .racesWrapper {
            position: relative;
            height: 100vh; /* Adjust based on the number of slides */
            overflow: hidden;
            transition: background-color 1s ease; /* Add this line for smooth color transition */
        }

        .swiper-container {
            height: 100%;
        }

        .swiper-wrapper {
            display: flex;
            flex-direction: row;
        }

        .swiper-slide {
            font-family: 'Staatliches', cursive;
            display: flex;
            align-items: center;
            justify-content: center;
            text-align: center;
            height: 80vh;
            width: 100vw; /* Full width for horizontal scroll */
        }

        .button-container {
            position: absolute;
            top: 10px;
            z-index: 1000;
            display: flex;
            justify-content: space-around;
            width: 100%;
        }

        .button-container button {
            display: block;
            margin-bottom: 5px;
            width: 24%;
            height: 50px;
        }

        .progress-bar {
            position: absolute;
            bottom: 0;
            left: 0;
            height: 5px;
            background: #333;
            width: 100%;
            z-index: 1000;
        }

        .progress-bar-inner {
            height: 100%;
            background: #e10600;
            width: 0;
            transition: width 0.2s ease, background-color 0.2s ease;
        }

        /* Content styling start */
        .swiper-slide .row {
            width: 100%;
            height: 100%;
            margin: 0 auto;
        }

        .swiper-slide .col-md-6 {
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: flex-start;
            padding-inline: 50px;
        }

        .swiper-slide .col-md-6 :is(p, h2) {
            text-align: left;
        }

        .swiper-slide .img-wrapper {
            width: 75%;
        }

        .swiper-slide .img-col {
            align-items: center !important;
            justify-content: center !important;
        }

        .swiper-slide a.cta-btn {
            background: #222;
            color: white;
            padding: 10px 20px;
            border-radius: 5px;
        }
    </style>
</head>
<body>
    <div class="racesWrapper">
        <!-- Swiper -->
        <div class="swiper-container">
            <div class="swiper-wrapper">
                <div class="swiper-slide">
                    <div class="row">
                        <div class="col-md-6">
                            <h2>Unbeatable Performance 14X Faster Speed</h2>
                            <p>Devrims provides enterprise-grade, managed cloud servers tailored to your specific website and project requirements. The platform offers high performance storage, lightweight technology stacks optimized for PHP applications.</p>
                            <a class="cta-btn">Start Free Trial</a>
                        </div>
                        <div class="col-md-6 img-col">
                            <div class="img-wrapper">
                                <img src="https://images.unsplash.com/photo-1551288049-bebda4e38f71?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D">
                            </div>
                        </div>
                    </div>
                </div>
                <div class="swiper-slide">Flexibility</div>
                <div class="swiper-slide">Reliability</div>
                <div class="swiper-slide">Costing</div>
            </div>
        </div>
        <div class="progress-bar">
            <div class="progress-bar-inner"></div>
        </div>
        <div class="button-container">
            <button onclick="goToSlide(0)">Performance</button>
            <button onclick="goToSlide(1)">Flexibility</button>
            <button onclick="goToSlide(2)">Reliability</button>
            <button onclick="goToSlide(3)">Costing</button>
        </div>
    </div>

    <script src="https://unpkg.com/swiper/swiper-bundle.min.js" defer></script>
    <script src="https://unpkg.com/gsap@3/dist/gsap.min.js" defer></script>
    <script src="https://unpkg.com/gsap@3/dist/ScrollTrigger.min.js" defer></script>
    <script src="https://unpkg.com/gsap@3/dist/ScrollToPlugin.min.js" defer></script>
    <script>
        document.addEventListener('DOMContentLoaded', function() {
            gsap.registerPlugin(ScrollTrigger, ScrollToPlugin);

            const swiper = new Swiper('.swiper-container', {
                direction: 'horizontal',
                loop: false,
                speed: 1000,
                slidesPerView: 1,
                spaceBetween: 0,
            });

            function getSwiperWrapperWidth() {
                return document.querySelector(".swiper-wrapper").scrollWidth;
            }

            const horizontalScroll = gsap.to(".swiper-wrapper", {
                xPercent: -100 * (swiper.slides.length - 1),
                ease: "none",
                scrollTrigger: {
                    trigger: ".racesWrapper",
                    pin: true,
                    scrub: 1,
                    markers: true,
                    start: 'top top',
                    end: () => `+=${getSwiperWrapperWidth()}`,
                    invalidateOnRefresh: true,
                    onUpdate: self => {
                        const progress = self.progress;
                        document.querySelector(".progress-bar-inner").style.width = `${progress * 100}%`;

                        const colorIndex = Math.floor(progress * (colors.length - 1));
                        document.querySelector(".racesWrapper").style.backgroundColor = colors[colorIndex];
                        gsap.to(".racesWrapper", {
                            backgroundColor: colors[colorIndex],
                            duration: 0.05, // Adjust duration if necessary
                            ease: "power1.inOut" // Smooth easing function
                        });
                    }
                }
            });

            const colors = [
                "#DADDEF", "#D6DAEE", "#D2D5EC", "#CDD3EB", "#CACFE9",
                "#C6CAE7", "#C1C7E5", "#BDC3E3", "#B9C0E1", "#ADB5DC",
                "#A9B1DA", "#A5AED8", "#A2ABD6", "#9DA6D4", "#98A3D2",
                "#919CCF", "#9EA4D3", "#A7AAD5", "#B1AEC8", "#BCB2B6",
                "#C8B7A3", "#D3BB91", "#DFC07D", "#EAC36A", "#F6C85A",
                "#FFCC4A", "#F3C264", "#E9B780", "#DDAC9C", "#D2A1B9",
                "#C697D2", "#BB8BEF", "#B384FE", "#B384FE", "#B384FE",
                "#B384FE", "#B384FE", "#B384FE"
            ];

            function updateProgressBar() {
                const progress = (swiper.activeIndex / (swiper.slides.length - 1)) * 100;
                document.querySelector(".progress-bar-inner").style.width = `${progress}%`;

                const colorIndex = Math.floor((swiper.activeIndex / (swiper.slides.length - 1)) * (colors.length - 1));
                document.querySelector(".racesWrapper").style.backgroundColor = colors[colorIndex];
                gsap.to(".racesWrapper", {
                    backgroundColor: colors[colorIndex],
                    duration: 0.05, // Adjust duration if necessary
                    ease: "power1.inOut" // Smooth easing function
                });
            }

            function goToSlide(index) {
                // Slide to the desired index
                swiper.slideTo(index);

                // Define the swiper wrapper width
                const swiperWrapperWidth = 5616;

                // Determine the scroll position based on the button clicked
                let scrollPosition;
                switch (index) {
                    case 0:
                        // Fixed scroll position for the first button
                        scrollPosition = 1400;
                        break;
                    case 1:
                        // 26% of the swiper wrapper width
                        scrollPosition = 3108;
                        break;
                    case 2:
                        // 52% of the swiper wrapper width
                        scrollPosition = 4812;
                        break;
                    case 3:
                        // 76% of the swiper wrapper width
                        scrollPosition = 6520;
                        break;
                    default:
                        // Default to the calculated position for other slides
                        scrollPosition = (index / (swiper.slides.length - 1)) * swiperWrapperWidth;
                        break;
                }

                // Log the scroll position for debugging
                console.log("Scroll Position: ", scrollPosition);

                // Smoothly scroll to the calculated position
                gsap.to(window, {
                    scrollTo: { 
                        y: scrollPosition, 
                        autoKill: false 
                    },
                    duration: 0.5,
                    ease: "linear"
                });

                // Update the progress bar and background color
                updateProgressBar();

                // Refresh ScrollTrigger to account for changes
                ScrollTrigger.refresh();
            }

            swiper.on('slideChange', () => {
                updateProgressBar();
            });

            // Initial update
            updateProgressBar();

            // Make goToSlide function accessible globally
            window.goToSlide = goToSlide;
        });
    </script>
</body>
</html>

Upvotes: 1

Related Questions