Skip to content

v7.2+ Infinite Scroll Implementation

This section describes the infinite scroll implementation in the project, designed to be reusable and extensible.

Main Components

  • infinite-scroll/infinite-scroll.js:
    • This module handles the main logic of infinite scroll.
    • It uses IntersectionObserver to detect when the user reaches the end of the page.
    • It inserts the new content into the page and updates the URL.
    • It emits the InfiniteScroll event when new content is loaded, allowing other modules to react.
  • infinite-scroll/embed-scripts.js:
    • Listens for the InfiniteScroll event and loads social media embed scripts (Twitter, Facebook, Instagram, TikTok) as needed.
    • It uses an extensible configuration (infinite-scroll/embed-configurations.js) to define which scripts to load and under what conditions.
  • infinite-scroll/embed-configurations.js:
    • Defines the configuration for loading embed scripts.
    • Allows extending and modifying the configuration from other modules.
  • infinite-scroll/dom-utils.js
    • a util that can be used to initialize JS code (ads, carousels, etc.) both for the current article and for subsequently loaded articles. Read more.
  • InfiniteScrollManager:
    • Handles requests to Elasticsearch to obtain additional pages. By default, the query is searching for the same category and template.
  • InfiniteScrollController and InfiniteScroll/index.html.twig:
    • Generate the JSON that contains the information of the pages to be loaded in the infinite scroll.

Functioning Flow

  1. Initialization:

    • infinite-scroll/infinite-scroll.js is initialized on our project, configuring the IntersectionObserver and loading the information of the current page and the following ones from the JSON generated by InfiniteScrollController. Example of how initialize infinite scroll on the project src/App/Bundle/CmsBundle/Resources/public/javascripts/app/article.js. Just pass as parameter in the initialize function the querySelector where intersectionObserver has to watch to load new articles
    js
    require([
    "../src/main",
    "wfcb/infinite-scroll/index",
    "wfcb/infinite-scroll/embed-loader",
    ], function (_, infiniteScroll) {
        document.addEventListener("DOMContentLoaded", function () {
        infiniteScroll.initialize("footer");
    });
    });
  2. Scroll Detection:

    • When the user scrolls and the IntersectionObserver detects that the end of the page is visible, the loading of new content is activated.
  3. Content Request:

    • infinite-scroll/infinite-scroll.js uses the JSON generated by InfiniteScrollController::indexAction (that, in turn, obtains this data from InfiniteScrollManager::getInfiniteScrollData) to obtain the following pages. The JSON generated is similar to this:
    html
    <div data-type="infiniteScroll">
        <script type="application/json">
        {
          "currentPage":{
            "url":"http://primicias.local:8082/prueba/tce-anula-proceso-destitucion-john-vinueza-alcalde-riobamba-437/",
            "id":437,
            "hiddenElements":[
              "header",
              ".c-footer"
            ],
            "insertAfterSelector":"main",
            "shownSelectors":"section",
            "title":"TCE anula proceso de destitución contra John Vinueza, alcalde de Riobamba"
        },
        "nextPages":[
          {
           "url":"http://primicias.local:8082/prueba/noticia-embeds-439/",
            "id":439,
            "hiddenElements":[
              "header",
              ".c-footer"
            ],
            "insertAfterSelector":"main",
            "shownSelectors":"section"
          },
          // ...Rest of pages, 5 by default
        ]}
        </script>
    </div>
    • Explanation:
      • url: URL of the page.
      • id: Page ID.
      • hiddenElements: Selectors of elements to hide in the new content.
      • insertAfterSelector: Selector of the element after which to insert the new content.
      • shownSelectors: Selector of the element containing the new content.
      • title: Page title.

    The data for each page is being composed by InfiniteScrollManager::getPageData. Check this service's code in order to see how to overwrite/extend the data that is returned.

  4. Content Insertion:

    • The new content is inserted into the page after the last article.
    • The event “InfiniteScroll” is dispatched with the document fragment loaded.
  5. Embed Loading:

    • infinite-scroll/dom-utils.js listens for the InfiniteScroll event and load scripts previously set in your application. For example we have our main.js like this and we want to load carousels in the loaded pages:
    js
        var initializeCarousels = function (element) {
        fnCarousel1items(element);
        fnCarouselTemasDetacados(element);
        fnCarousel1itemsSponsoredContent(element);
        fnCarousel1itemsOnlyDots(element);
        fnCarouselVideosRecomendados(element);
        fnCarousel1itemsSpecialsCardsRelevant(element);
        fnCarousel3items(element);
        fnCarousel4items(element);
    };
    
    var initializeInteractions = function (element) {
        openSideBar.init(element);
        closeSideBar.init(element);
        toggleSubmenu.init(element);
        scrollToTop.init(element);
        openModals.init(element);
        closeModals.init(element);
        closeModalsOutside.init(element);
        closeModalsPresEsc.init(element);
    };
    
    var initializeCookies = function (element) {
        generateCookies(element);
    };
    
    $(document).ready(function () {
        initializeCarousels(document);
        initializeInteractions(document);
        initializeCookies(document);
    });

    We can import infinite-scroll/dom-utils

    js
    define([
    "wfcb/infinite-scroll/dom-utils",
    ], function (domUtils) 
    
    //Rest of code..
    
    //Add this lines and call methods you want to reload in the new pages laoded
    domUtils.forEach("article", initializeCarousels);
    domUtils.forEach("article", initializeInteractions);
  • infinite-scroll/embed-scripts.js listens for the InfiniteScroll event and, if it finds social media embeds in the new content, loads the necessary scripts.
  1. URL / Title Update:
    • The URL and title is updated to reflect the current page in the scroll.

Extensibility

  • Embeds:
    • The embed loading configuration in infinite-scroll/embed-configurations.js can be extended or modified from other modules.
  • Events:
    • Other modules can listen for the InfiniteScroll event to perform additional actions when new content is loaded.
  • Scroll pages data:
    • InfiniteScrollManager::getPageData receives the PageArticle argument and returns the array that will be exposed to JS in JSON format.

Usage

To integrate infinite scroll into a project, make sure that:

  • The JSON generated by InfiniteScrollController is available on the page. overwrite the base block in src/App/Bundle/CmsBundle/Resources/views/Article/show.html.twig.

      {% block wf_inifinte_scroll %}
        {{ render_esi( url('wf_article_next_article', {
            page: page.id,
            category: category.id,
            template: page.template,
            numPages: 5
        }) ) }}
    {% endblock %}
  • The js files are loaded property in article.js of your project.

js
    require([
    "wfcb/infinite-scroll/index",
    "wfcb/infinite-scroll/embed-loader",
    ], function (infiniteScroll) {
        document.addEventListener("DOMContentLoaded", function () {
        infiniteScroll.initialize("footer");
    });
    });
  • The HTML elements that contains the social media embeds contains the necesary classes to be detected by the infinite-scroll/embed-scripts.js file.

dom-utils

Intro

infinite-scroll/dom-utils.js is a small utility that makes it easier to initialize JavaScript code (e.g.: ads, carousels, etc.) both for the currently loaded article and also for subsequently loaded articles.

It applies the given callback for the existing HTML, loaded from the server, and also listens for the InfiniteScroll event to apply the same functions for the articles that are loaded through AJAX as part of infinite scroll.

Usage

forEach

domUtils.forEach(selector, callback): invokes callback for all the elements that match selector. It invokes it first for the elements in the initial article and calls callback for every article that is loaded through AJAX as part of initinite scroll.

javascript
define(["wfcb/infinite-scroll/dom-utils",], function(domUtils) {
    domUtils.forEach(".gallery", function(galleryEl) {
        // initialize the gallery for this element
    })
})

on

domUtils.on(eventName, selector, callback): invokes callback whenever eventName is trigered on elements that match selector. It initially hooks up all the elements for the initial article and also for articles that are subsequently loaded through AJAX as part of infinite scroll:

javascript
define(["wfcb/infinite-scroll/dom-utils",], function(domUtils) {
    domUtils.on("click", ".zoom-button", function(zoomButtonEl) {
        // the `.zoom-button` has been clicked, 
        // open a modal for the image associated to it
    })
})