Lazy Loading

Last modified by Marius Dumitru Florea on 2021/08/24

One common and recurrent need when working on the front-end is to be able to load some UI elements after the page has been loaded. This technique is called lazy loading and it's very useful if you have UI elements that don't need to be visible right away, especially if they are heavy and thus would slow down the page load. We use this for instance to:

  • load the content of a modal dialog box on demand, when the modal is first shown
  • load the content of a tab when the tab is first activated
  • load the content of a panel after the rest of the page has been loaded

In order to apply this technique you need to have:

  • a JavaScript code that makes an asynchronous HTTP request to get the UI element that is being lazy loaded
  • a server side script / service that generates and returns the HTML of the requested UI element

The JavaScript code usually looks like this:

require(['jquery'], function($) {
 var lazyLoad = function(container) {
   var serviceURL = ...
    container.empty().addClass('loading');
   return $.get(serviceURL).done(function(html) {
     // Inject the HTML received from the server.
     container.html(html);
     // Let the others know that the DOM has been updated in order to enhance it.
     $(document).trigger('xwiki:dom:updated', {'elements': container.toArray()});
    }).always(function() {
      container.removeClass('loading');
    });
  };
});

Note that triggering the xwiki:dom:updated event is very important because it allows the loaded UI element to be enhanced by other JavaScript modules (some of which may have been also lazy loaded along with the UI element). XWiki 13.7-rc-1+ Note also that we don't have to care about loading / injecting the JavaScript or CSS required by the UI element. This is done automatically by the XWiki platform, with the help of the server side, as we'll see below.

A JavaScript module that enhances UI elements that are being lazy loaded looks like this:

require(['jquery'], function($) {
 var init = function(event, data) {
   var container = $((data && data.elements) || document);
    container.find('.some-selector').each(function() {
     // Enhance the matched UI elements.
     ...
    });
  };

 // Initialize UI elements that are lazy loaded.
 $(document).on('xwiki:dom:updated', init);
 // Initialize UI elements that are already loaded (present on the page).
 $(init);
});

For the server side we use most of the time:

  • either a script rendering macro (e.g. Velocity Macro) within a wiki page
    var documentReference = XWiki.Model.resolve('Path.To.Page', XWiki.EntityType.Document);
    var serviceURL = new XWiki.Document(documentReference).getURL('get');
  • or a Velocity template
    var serviceURL = XWiki.currentDocument.getURL('get', 'xpage=templateName');

If we chose to implement the server side in a wiki page using the Velocity macro then the code could look like this:

#template('display_macros.vm')
#initRequiredSkinExtensions()
## Generate the HTML for the requested UI element.
...
## Use the skinx plugin to pull required skin extensions (JavaScript and CSS).
#set ($discard = $xwiki.ssx.use('Path.To.Style'))
#set ($discard = $xwiki.jsx.use('Path.To.JavaScript'))
...
#getRequiredSkinExtensions($requiredSkinExtensions)
## We use the X-XWIKI-HTML-HEAD custom HTTP header to return the required JavaScript and CSS resources. Note that the
## HTML of the UI element is returned in the response body.
#set ($discard = $response.setHeader('X-XWIKI-HTML-HEAD', $requiredSkinExtensions))

XWiki 13.7-rc-1+ The resources we specify using the X-XWIKI-HTML-HEAD custom HTTP header will be injected automatically in the page head.

Tags:
   

Get Connected