অসীম স্ক্রল

এই অসীম স্ক্রোল বাস্তবায়নটি নিশ্চিত করার জন্য ডিজাইন করা হয়েছে যে কোনও লেআউট পরিবর্তন হবে না - তা নির্বিশেষে নতুন বিষয়বস্তুর সাথে প্রতিক্রিয়া জানাতে সার্ভারের কত সময় লাগে।

অনেক অসীম স্ক্রোল বাস্তবায়নের সাথে সবচেয়ে সাধারণ সমস্যাগুলির মধ্যে একটি হল যে পৃষ্ঠার ফুটার (বা অনুরূপ ইউএক্স উপাদান) যখনই নতুন আইটেম যোগ করা হয় তখন পৃষ্ঠার আরও নিচে ঠেলে দেওয়া হয়। এই অসীম স্ক্রোল বাস্তবায়নের সাথে, এটি কখনই ঘটে না।

উচ্চ-স্তরের পদ্ধতি

যখনই সম্ভব, ব্যবহারকারীর কাছে পৌঁছানোর আগে নতুন আইটেমগুলি পৃষ্ঠায় ঢোকানো হয়। যেহেতু এই সন্নিবেশটি অফস্ক্রিনে ঘটে (এবং ব্যবহারকারীর কাছে দৃশ্যমান নয়), ব্যবহারকারীর কোনো লেআউট পরিবর্তনের অভিজ্ঞতা হয় না।

ইভেন্টে যে নতুন বিষয়বস্তু সময়মতো ঢোকানো যাবে না, পরিবর্তে একটি "আরো দেখান" বোতাম প্রদর্শিত হয়। যাইহোক, বোতামটি তখনই সক্রিয় করা হয় যখন নতুন আইটেমগুলি প্রদর্শনের জন্য প্রস্তুত থাকে - এটি নিশ্চিত করে যে ব্যবহারকারী বোতামটি ক্লিক করবেন না শুধুমাত্র এটি খুঁজে বের করার জন্য যে কিছুই ঘটে না৷ সুতরাং, সার্ভারটি নতুন বিষয়বস্তুর সাথে কত ধীরে সাড়া দেয় (বা ব্যবহারকারী কত দ্রুত স্ক্রল করে) তা নির্বিশেষে, কোনও অপ্রত্যাশিত লেআউট পরিবর্তন হবে না।

বাস্তবায়ন

ইন্টারসেকশন অবজারভার API হল পৃষ্ঠার উপাদানগুলির অবস্থান এবং দৃশ্যমানতা নিরীক্ষণ করার একটি কার্যকর উপায়। এই নকশা দুটি পৃথক ইন্টারসেশন পর্যবেক্ষক ব্যবহার করে প্রয়োগ করা হয়:

  • listObserver অসীম স্ক্রোল তালিকার শেষে অবস্থিত #infinite-scroll-button অবস্থান পর্যবেক্ষণ করে। যখন বোতামটি ভিউপোর্টের কাছাকাছি থাকে, তখন সন্নিবেশিত বিষয়বস্তু DOM-এ যোগ করা হয়।
  • sentinelObserver #sentinel উপাদানটির অবস্থান পর্যবেক্ষণ করে। যখন সেন্টিনেল দৃশ্যমান হয়, সার্ভার থেকে আরও সামগ্রীর অনুরোধ করা হয়। সেন্টিনেলের অবস্থান সামঞ্জস্য করা হল সার্ভার থেকে নতুন সামগ্রীর জন্য কতটা আগে অনুরোধ করা উচিত তা নিয়ন্ত্রণ করার একটি উপায়।

এটি অসীম স্ক্রোল ব্যবহার থেকে উদ্ভূত বিন্যাস পরিবর্তনের সমাধান করার একমাত্র উপায় নয়। এই সমস্যাটির সাথে যোগাযোগ করার অন্যান্য উপায়গুলির মধ্যে রয়েছে পেজিনেশনে স্যুইচ করা, তালিকা ভার্চুয়ালাইজেশন ব্যবহার করা এবং পৃষ্ঠা লেআউট সামঞ্জস্য করা।

এইচটিএমএল

<div id="infinite-scroll-container">
    <div id="sentinel"></div>
    <div class="item">A</div>
    <div class="item">B</div>
    <div class="item">C</div>
    <div class="item">D</div>
    <div class="item">E</div>
    <button id="infinite-scroll-button" disabled>
        <span class="disabled-text">Loading more items...</span>
        <span class="active-text">Show more</span>
    </button>
</div>

সিএসএস


        :root {
    --active-button-primary: #0080ff;
    --active-button-font:#ffffff;
    --disabled-button-primary: #f5f5f5;
    --disabled-button-secondary: #c4c4c4;
    --disabled-button-font: #000000;
}
#infinite-scroll-container {
    position: relative;
}
#sentinel {
    position: absolute;
    bottom: 150vh;
}
#infinite-scroll-button {
    cursor: pointer;
    border: none;
    padding: 1em;
    width: 100%;
    font-size: 1em;
}
#infinite-scroll-button:enabled {
    color: var(--active-button-font);
    background-color: var(--active-button-primary)
}
#infinite-scroll-button:disabled {
    color: var(--disabled-button-font);
    background-color: var(--disabled-button-primary);
    cursor: not-allowed;
    animation: 3s ease-in-out infinite loadingAnimation;
}
#infinite-scroll-button:enabled .disabled-text {
    display: none;
}
#infinite-scroll-button:disabled .active-text {
    display: none;
}
@keyframes loadingAnimation {
    0% {
        background-color: var(--disabled-button-primary);
    }
    50% {
        background-color: var(--disabled-button-secondary);
    }
    100% {
        background-color: var(--disabled-button-primary);
    }
}
        

জেএস


        function infiniteScroll() {
    let responseBuffer = [];
    let hasMore;
    let requestPending = false;
    const loadingButtonEl = document.querySelector('#infinite-scroll-button');
    const containerEl = document.querySelector('#infinite-scroll-container');
    const sentinelEl = document.querySelector("#sentinel");
    const insertNewItems = () => {
        while (responseBuffer.length > 0) {
            const data = responseBuffer.shift();
            const el = document.createElement("div");
            el.textContent = data;
            el.classList.add("item");
            el.classList.add("new");
            containerEl.insertBefore(el, loadingButtonEl);
            console.log(`inserted: ${data}`);
        }
        sentinelObserver.observe(sentinelEl);
        if (hasMore === false) {
            loadingButtonEl.style = "display: none";
            sentinelObserver.unobserve(sentinelEl);
            listObserver.unobserve(loadingButtonEl);
        }
        loadingButtonEl.disabled = true
    }
    loadingButtonEl.addEventListener("click", insertNewItems);
    const requestHandler = () => {
        if (requestPending) return;
        console.log("making request");
        requestPending = true;
        fakeServer.fakeRequest().then((response) => {
            console.log("server response", response);
            requestPending = false;
            responseBuffer = responseBuffer.concat(response.items);
            hasMore = response.hasMore;
            loadingButtonEl.disabled = false;;
        });
    }
    const sentinelObserver = new IntersectionObserver((entries, observer) => {
        entries.forEach(entry => {
            if (entry.intersectionRatio > 0) {
                observer.unobserve(sentinelEl);
                requestHandler();
            }
        });
    });
    const listObserver = new IntersectionObserver((entries, observer) => {
        entries.forEach(entry => {
            if (entry.intersectionRatio > 0 && entry.intersectionRatio < 1) {
                insertNewItems();
            }
        });
    }, {
        rootMargin: "0px 0px 200px 0px"
    });
    sentinelObserver.observe(sentinelEl);
    listObserver.observe(loadingButtonEl);
}
infiniteScroll();