Partial Loading

The capability to load, reload, update part of a web page is key to Sircl. It allows updating the users view by letting the server do the rendering without triggering a full page load. This section explains the different ways to use partial loading with Sircl.

Introduction

Sircl offers the capability to update parts of pages without the need of a full page reload. It does this by issuing Ajax requests, but differentiates from classical JavaScript frameworks by the declarative way i which partial page loading is defined.

Sircl can issue Ajax requests to load parts of a page in the following cases:

  • Automatically, for each onload-load attribute
  • Activating a hyperlink (or any element with a href attribute) with an "inline target"
  • Submitting a form with an inline target
  • When triggering an event-action that performs a request for a page part to the server (i.e. a viewport, change or drop action configured to issue a server request)

In response to those Ajax requests, the server is expected to produce and return HTML document fragments.

The first three cases are discussed in this chapter.

Automatic page part loading

With automatic page part loading, you can instruct to load or update part of a page as soon as it is loaded.

One-time loading

As soon as a page or page part is loaded, Sircl will search the loaded part for elements with an onload-load attribute, and issue an Ajax request using the attributes value as URL. The servers response is the injected in the element.

Example, consider the following code:

<div onload-load="/References/List">
  Please wait...
</div>

When loaded, the DIV will initially show the message "Please wait...". But immediately, a new request will be sent to the server to retrieve a list of references, and the content returned by the server will be injected in the DIV element, replacing the "Please wait..." message.

Though the browser cache is diabled by default by Sircl for Ajax calls, any occurence of "{rnd}" (the string "rnd" between curly braces) will be replaced by a random number before issuing the call, as an option to fool browser, server or proxy caches. I.e:

<div onload-load="/References/List?{rnd}">
  Please wait...
</div>

Tip: instead of showing a "Please wait" textual message, you can show an animated spinner to provide better visual feedback.

Repeated reloading

You can extend an element having an onload-load attribute, with an onload-reloadafter attribute, specifying the reload interval in seconds. I.e:

<div onload-load="/Account/BalanceInfo" onload-reloadafter="60">
  Please wait...
</div>

In this example, as soon as the page is loaded, a request is made to get balance information. That request is re-issued every 60 seconds to update the balance information.

At any time, the server could decide the client should stop reloading this part by returning together with the content of the last part a X-Sircl-Reload-After response header with a value less or equal to 0:

X-Sircl-Reload-After: 0

Note: Use repeated reloading with care. You should not overload your server with too many requests.

When reloading a page part, the HTML content is replaced every time. To prevent the HTML to be updated if it did not change, add a diffcheck class on the target element.

Instructed reloading

It is also possible to instruct reloading of an element having an onload-load attribute when a different part of the page is loaded.

Consider the following HTML fragment that could appear in the page header:

<span id="msgcounter" class="badge" onload-load="/Profile/MessageCounter"></span>

It's initially an empty span element, but it gets filled with a message counter value using an Ajax request.

Whenever at any time, anywhere in the page, a part is loaded that contains an element with an onload-reload attribute of which the value is a CSS selector matches the span element, a reload of the content is performed using the URL in the span element's onload-load attribute. For instance, if on another place on the document the following HTML fragment is loaded, the message counter will be updated:

<p onload-reload="#msgcounter">Hello!</p>

This fragment will show the message "Hello !", but also reload the element matching the "#msgcounter" selector.

Instructed reloading from server

It is also possible to instruct reloading an element from the server without rendering an onload-reload attribute.

For this the server should return a response with the following response header:

X-Sircl-Load: #msgcounter

Or, the server can return an X-Sircl-Reload-After response header with a float value larger than 0 expressed in seconds to request the currently loading part to be reloaded once after the given timeout. 

To request the currently loading part to be reloaded after 4 seconds, the server should return the following response header:

X-Sircl-Reload-After: 4

Notice the difference: the X-Sircl-Load header allows reloading a different part of the page, but the load is initiated immediately, while the X-Sircl-Reload-After header allows for delayed reload of the currently loading part only.

The X-Sircl-Reload-After response header also allows to abort a repeated reloading by having a value of 0 or less.

To reload the whole page, use the X-Sircl-History header with value "reload" and set the X-Sircl-Target header to the value "_self":

X-Sircl-History: reload
X-Sircl-Target: _self

When to use

Automatic loading is typically used in the following scenarios:

  • Provide a better performance experience to the enduser by delaying the rendering of information that is expensive to retrieve.
    In a classic approach, the browser always requests full pages from the server. The server builds the complete page, performing all necessary retrievals, queries and calculations, and then returns the whole page when alle data has been gathered and rendered in HTML format.
    With Sircl's automatic loading, the server can return a page with only fast-to-retrieve information, and then have the browser issue separate requests (in parallel)  to retrieve the more expensive parts. As a result, the enduser gets a first response quicker and experiences the website/app as performing better.

  • Integrate parts (like widgets or components) from other sources into this page. This way, for instance, a "search widget" can be reused accross different applications without need to share the code.

  • Have a part of the page automatically polling for information of the server, i.e. the finalization of a long process, until a final response is returned.

  • Enhance cachability and resilience of resources.
    Proxy services as CloudFlare or CDN77 can keep your site alive even if your server is down, but they can do that only for pages that do not contain visitor specific data, such as the name of the logged in user in the top banner. Using automatic loading you can transform pages into cachable, sharable resources in which visitor-specific parts are injected, allowing proxy services to keep larger parts of your site alive.

  • Updating parts of the page out of scope of the current request.
    For instance, in Single Page mode, you typically have a static page frame that may contain profile information (logged in username, number of messages, etc). When a user logs in, the page frame should still remain but parts of it can be updated with automatic reloading.

Page part loading by hyperlink

Hyperlinks are used to navigate from one page to the other. With Sircl, they can also be used to load or update part of the current page.

Inline targets

Sircl uses the target of the hyperlink to distinct "normal" hyperlinks that let the browser navigate to another page from "part loading" hyperlinks that load part of the current page.

For a hyperlink to load a part inside the current page, the hyperlink must have a target attribute set to a CSS selector indicating the element of the page to be loaded.

A target containing a CSS selector is named an inline target, and is identified by its first character that must be a "#", ".", "*", ":", "<", ">", "$", "[" or a space.
Any target not starting with one of those characters will be considered a "normal" target pointing to a specific browser tab, window or frame.

Also, a hyperlink having a download attribute will always be treated as a "normal" hyperlink.

I.e. the following hyperlinks are treated as "normal" hyperlinks and will result in the browser performing a regular (non-Ajax) request for a full page:

<a href="/any" target="_top">...</a>
<a href="/any" target="Tab21">...</a>
<a href="/any" target="$T35" download>...</a>

Following hyperlinks are considered to have inline targets and will be intercepted by Sircl:

<a href="/any" target="#id65">...</a>
<a href="/any" target=" P[class='t1']">...</a>

For these hyperlinks, Sircl will issue an Ajax request and inject the response in the element matching the inline target CSS selector value.

In Multi-Page mode (the default), a hyperlink with no target attribute will be treated as a "normal" hyperlink:

<a href="/any">...</a>

Also, in Multi-Page mode, the URL of the browser is not changed when navigating hyperlinks with inline targets. And therefore, deep linking to those hyperlinks is not supported.

Example:

<div id="moreinfo">
  <a href="/MoreInfo" target="#moreinfo">Click here for more information</a>
</div>

Clicking the "Click here for more information" hyperlink will replace the content of the surrounding DIV with the response received from the server when issueing the "/MoreInfo" request.

Relative CSS selectors

Sircl extends the syntax of CSS selectors with the capability to refer to elements relatively to the current element.

Read more about this in the chapter Relative CSS Selectors.

Field substitution

The value of the href attribute is usually a URL. You can make the URLs more dynamic by using field substitution. Field substitution allows you to insert values of form fields in the URL of hyperlinks.

For field substitution to work, the element holding the href attribute (or onclick-load attribute as covered further), must also have the class substitute-fields. The URL can then contain references to other fields by placing their name between square brackets ([ and ]).

In the following example, users can select a country and then ask for more information about that country:

<select name="country">
  <option value="Belgium">Belgium</option>
  <option value="France">France</option>
  <option value="Germany">Germany</option>
</select>
<br/>
<a class="substitute-fields" href="https://en.wikipedia.org/wiki/[country]">More about this country.</a>

Since the anchor has the substitute-fields class, the URL will be searched for field names between square brackets. If the hyperlink is inside a form, then only that form is searched for a matching field. If the hyperlink is not part of a form, the whole document will be searched. When more than one matching field is found, only the value of the first one will be substituted.

As a result, if the user selected "France" before clicking the hyperlink, a request would be issued to the following URL:

https://en.wikipedia.org/wiki/France

Special href values

With Sircl, href attributes can have additional special values that do not trigger page or partial page loading:

  • <a href="null">
    or
    <a href="">
    Hyperlink is ignored.

  • <a href="history:back">
    Let the browser navigate back to the previous page in the history.

  • <a href="history:reload">
    Let the browser reload the current page.

  • <a href="alert:Hello World">
    Shows the message "Hello World" in an alert box.

  • <a href="javascript:alert('Hello World');">
    Executes the given JavaScript code, showing the message "Hello World" in an alert box.
    The element can also have a nonce attribute.

The history:back link will have no effect if there is no previous page. If it is the only page in the browser history (for instance resulting from a right-click Open link in new tab), and the link has an onback-allowclose attribute with a message text, then the message text will be propsed in a confirm dialog and upon confirmation, the current browser tab or window will be closed. If the link has an onback-allowclose class, the current browser tab or window will be closed without confirmation request.

Any element

Sircl extends the hyperlink behaviour to any element having an href attribute.

For instance, if you add an href attribute to a TR element, clicking the table row will navigate the hyperlink:

<table>
  <tr href="/ProductInfo/23">
    <td>...</td>
  </tr>
  <tr href="/ProductInfo/24">
    <td>...</td>
  </tr>
</table>

Since the TR elements have a href attribute but no target attribute, clicking a row will result (in the default Multi-Page mode) in loading the full page referred by the href attribute.

Note: Be aware however that search engines will most often ignore those hyperlinks. As a result, pages referred by hyperlinks that do not use the A tag, will not be properly indexed. Also, users will not be able to right-click and choose "Open in new window" in their browser for hyperlinks that do not use the A tag. If SEO is important for your project, or the user should be able to open the links in new browser tabs, you should avoid using href attributes on any elements other than A tags.

Onclick load event action

As an alternative to using the href attribute, you can also use the onclick-load attribute. It shows the same behaviour but can be styled differently.

Prefer the onclick-load attribute when the behaviour of a hyperlink is desired, without the look or styling of a hyperlink.

Page part loading by form submit

Analogous to part loading by hyperlink, a page part can also be the expected response of the submission of a form.

Here also, whether the form submit will be handled by Sircl to retrieve a page part, or by the browser to return a whole page, is determined by the target. An inline target will result in an Ajax call made by Sircl, while a window target will result in the form submission being handled by the browser.

<form action="/Add" target="#output">
  <input type="text" placeholder="Name" />
  <button class="submit">Add</button>
  <button class="submit" formaction="/Remove">Remove</button>
</form>
</div>
<div id="output"></div>

In this example, both Add and Remove buttons will result in submitting the form through Sircl, through an Ajax call. And the response of the server, assumed to be a part of a page, will replace the content of the DIV element with id "output", since that element has been set as target for the form.

The target can be defined in different ways. In order:

  1. Using a formtarget attribute on the submit trigger (submit button being pressed).
  2. Using the closest target attribute or target class on any element from the trigger up to the root of the DOM tree.
  3. If no explicit target is set, and Sircl runs in Single-Page mode, the default inline target is implied.

Note that window targets (non-inline targets) must follow the rules of regular HTML: they should use the formtarget attribute on the trigger, or the target attribute on the form.

A target class marks the element having the class as target. 

Example:

<div class="target">
  <form>
    <button class="submit">Button A</button>
    <button class="submit" formtarget="#elsewhere">Button B</button>
    <div target="#elsewhere">
      <button class="submit">Button C</button>
    </div>
  </form>
</div>
<div id="elsewhere"></div>

By pressing Button A, the DIV element surrounding the form will be used as inline target since it has a "target" class, and the button has no formtarget attribute.

By pressing Button B, the DIV element with id "elsewhere" will be used as inline target since it is referred to explicitely by the formtarget attribute.

By pressing Button C, the DIV element with id "elsewhere" will be used as inline target since the closest target class or attribute points to it.

Inline targets are ignored on forms that have the method attribute set to "dialog", or having a download attribute.

Note that when doing Ajax calls, Sircl will honor the form, formaction, formenctype, formmethod and formnovalidate attributes defined by standard HTML.

Page part loading by scrolling

Another way of loading a page part is by scrolling down. When an element has an ifinview-load attribute and that element is visible or becomes visible by scrolling, the attribute's value, a URL, is used to load and replace the element's content:

<div ifinview-load="/NextListOfProducts?page=2">
  Wait for loading...
</div>

When this element is visible, its content is replaced by the page part returned by the call to "/NextListOfProducts?page=2".

That page part can again containing an element with an ifinview-load attribute (i.e. to the URL "/NextListOfProducts?page=3") resulting in an apparantly infinite page.

Append or Replace methods

By default, the page part returned will replace the content of the target.

By setting the target-method attribute on either the target element or the element having the target class or attribute, the method applied when filling the target can be changed. Following methods are available:

  • "content" : the default method where the content of the target element is replaced by the new content;
  • "prepend" : the new content is prepended to the existing content of the target element;
  • "append" : the new content is appended to the existing content of the target element;
  • "replace" : the target element is removed and replaced by the new content.

In the following example, submitting the form will not replace the content of the FORM element, by replace the FORM element alltogether:

<form action="/GetTime" class="target" target-method="replace">
  <p>Current time is 16:45</p>
  <button type="submit">Refresh time</button>
</form>

The server can override the target method by setting the X-Sircl-Target-Method response header to either "content", "append", "prepend" or "replace".

When the target method is "replace", the original element (being replaced) has an id attribute, and it is t be replaced by a single element with no id, the id attribute is copied to the new element.

Sub-targets

When using page part requests, the server is to return new HTML content for the inline target element.

In some occasions, you may want to request new HTML content for an inline target, but only want to touch part of the target. In that case you can define a sub-target. The sub-target attribute takes a CSS selector that is used to identify the target element inside the target, that is allowed to be updated.

The attribute is placed on the trigger of the request (i.e. the hyperlink or the button used to submit the form), or, if a form is submitted, on the FORM element.

Take the following page part:

<form action="/Search" method="get" class="target" sub-target="#searchResults">
<input type="text" name="search">
  <button type="submit">Search</button>
  <div id="searchResults">
  Results...
  </div>
</form>

Despite the sub-target attribute, the server must still respond with the full new content for the form (as the form has a target class, it is itself the target for the submit request). But if both the original content of the target, and the new content provided by the server contain a match for the sub-target CSS selector, then only the content of the matched element in the original content is replaced with the content of the matched element in the new content.

In the above example, when the form is submitted, the server should respond with new content for the whole form, which could be the same form again but with search results. Due to the sub-target attribute, only the search results would be substituted.

Note that relative CSS selectors are not supported by the sub-target attribute, but the selector is always evaluated within the context of its parent target.

Combining partial page loads

To limit the number of requests to the server, multiple page parts can be combined in a single server response using the onload-moveto attribute.

Consider the following initial page:

<div id="d0" onload-load="/GetParts">
  Wait for loading...
</div>
<div id="d1"></div>
<div id="d2"></div>

The server could now use the call to "/GetParts" to provide content for all three DIV elements d0, d1 and d2, by for instance returning:

<h1>Main content</h1>
<div hidden onload-moveto="#d1">
 <h2>To D1</h2>
</div>
<div hidden onload-moveto="#d2">
  <h2>To D2</h2>
</div>

The whole response will be inserted in the DIV with id "d0". Then, the content will be processed, and the content of the element having the attribute onload-moveto set to "#d1" will be moved to the element with id "d1". Similarly, the content of the element having the onload-moveto attribute set to "#d2" will be moved to the element with id "d2".

The hidden attribute serves to avoid flickering, making sure the content to move to d1 and d2 is not first visible in d0.

Instead of moving a part, you can also instruct to copy a part using the onload-copyto attribute.

Load progression

Whenever Sircle handles server requests, that is whenever a server request has an inline target, various ways are available to show progression of the call.

Spinners

The submit element (typically a button) that triggered the submission of a form is known as the trigger. 

When a page part is loaded due to a clicked hyperlink (or any element having a href attribute) or when a form is submitted as a consequence of clicking a submit control (i.e. a submit button), the hyperlink or submit control is known as the trigger.

When the trigger contains an immediate child element with class spinner, that element will be substituted by a spinner when the page part request is initiated, and restored in its original state at the end of the request:

<form class="target">
  <button type="submit"><i class="spinner"></i> Click me!</button>
</form>

The following button is a working simulation:

Since the element having the spinner class is replaced when initiating the request, and restored afterwards, you can for instance put the spinner class on a (Bootstrap, Font Awesome or other) icon, such that the icon will be replaced by the spinner during the request:

<form class="target">
  <button type="submit"><i class="fas fa-cart-plus spinner"></i> Click me!</button>
</form>

Click this button for a working simulation:

The sircl library holds a default implementation of a spinner. The sircl-bootstrap4 and sircl-bootstrap5 libraries override that spinner to use a bootstrap style spinner. The sircl-fa library overrides the spinner to use a Font Awesome spinning icon.

You can override the spinner with the following Javascript line to be executed after loading the Sircl libraries:

sircl.html_spinner = '<span class="sircl-spinner spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> ';

It is hereby important that the spinner element has the sircl-spinner class.

Overlays

Each page part request has an inline target as described earlier. If that target contains an element with class overlay, that element will be made visible when a page part request is made to that target, and will be made hidden after the request is made:

<a href="/Update" target="#t1">Click me !</a>
<div id="t1">
  <div class="overlay" hidden></div>
  Click link to update
</div>

Note that the overlay has to be hidden if you do not want it to be initially visible.

Target load indication

When a request for a page part is initiated, the target element will receive the loading class. The class is removed when the call for a page part is done.

Body level load indication

When a request for a page part is initiated, the BODY element of the HTML page will receive the body-loading class. That class is also added on the beforeunload event of the page. The class is removed when the call for a page part is done.

You can use this in styling, for instance to make an element visible during page (part) loads:

<style>
  BODY .load-marker { display: none; }
  BODY.body-loading .load-marker { display: block; }
</style>

This will make any element with class load-marker anywhere on the page visible during load operations.

Progress bars

If the target element of a page part request has an upload-progress attribute, and its value is a CSS selector (extended CSS selectors are supported) to a PROGRESS element, that element will be made visible on initiating a page part request, and used to display upload progression. If the element was initially hidden, it will be hidden again after the request is terminated.

If the target element of a page part request has a download-progress attribute, and its value is a CSS selector (extended CSS selectors are supported) to a PROGRESS element, that element will be made visible on initiating a page part request, and used to display download progression. If the element was initially hidden, it will be hidden again after the request is terminated.

<a href="/Reload" target="#t1">Click me !</a>
<div id="t1" download-progress="#p1">
  Click the link to reload this data.
</div>
<progress id="p1" hidden />

Disable form re-submission

Hitting a form submit button repeatedly can cause the form to be submitted several times synchroneously (in parallel). To avoid this, and make sure the form can only be submitted one time at the time, the FORM element should have an onsubmit-disable attribute with a CSS selector (extended CSS selectors are supported) indicating which elements to disable during the request.

To disable all submit elements of the form, alternatively the form can have an onsubmit-disable class:

<form class="target onsubmit-disable">
  <button type="submit" name="action" value="1">Action 1</button>
  <button type="submit" name="action" value="2">Action 2</button>
</form>

When either the Action 1 or Action 2 button is pressed, both buttons (as they are both submit elements) will be disabled during the call to avoid re-submitting the form while a submission is in progress.

Disable hyperlinks

Similarly, hitting a hyperlink repeatedly may issue requests to the server faster than they can be processed. To disable a hyperlink for the duration of the call, the hyperlink element should have the onclick-disable class:

<a href="/News" target="#tnews" class="onclick-disable"> See news </a>

Sircl limits the styling of disabled hyperlinks to changing the cursor from pointer to default. Additional styling (graying out etc.) should be provided by the application. Following styling could be defined:

<style>
  [href][disabled] {
    color: gray;
    text-decoration: none; 
  }
</style>

Note that the onclick-disable class disables the holding element only for the duration of the request processing, while the onclick-disable attribute (see Event-Actions) permantently disables an element. To disable a hyperlink permantly after its first click, use the onclick-disable=":this" attribute.

On non-page part requests

The various ways to display load progression described here are meant for page part requests: requests that Sircl handles via Ajax to retrieve and update part of the current page.

Adding the onnavigate class to the trigger element (or one of its parents) will enable the spinner as well as the disabling of form and hyperlink submission for regular (full) page loads too. Note that the spinning and disabling will not be undone after the request. The browser is expected to replace the page which will remove all existing elements anyway. Forms having the method="dialog" and forms and hyperlinks having the download attribute are ignored.

<a href="https://www.example.com/" target="_self" class="onclick-disable onnavigate"><i class="spinner"></i> Example</a>

Server-side handling of partial page (Ajax) requests

Identifying page part requests

Page part requests are issued using AJAX. They resemble regular requests and expect regular HTML to be returned, except the returned HTML should not be a complete HTML document, but only part of it.

Often, the URL will determine the servers action and return value. You develop your site such that some URLs return full pages and some only parts of pages.

When using Single-Page mode, however, a same URL can expect a full page (when issued by the browser), or part of the page (when issued by AJAX). In that case, the server needs to distinct between both request types. You should not rely on the X-Requested-With header to be sent with value "XMLHttpRequest" as it is not always present: most browsers do not include this header on cross-domain requests for instance.

Sircl therefore adds its own request header to identify page part requests:

X-Sircl-Request-Type: Partial

If this header is present, it has the value "Partial", and it means Sircl made an AJAX call to retrieve part of a page.

Responding to page part requests

The server should respond by returning the requested HTML page part.

The following is a Node.js server responding with a page part consisting of a paragraph:

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.write('<p>This is just a paragraph</p>');
  res.end();
}).listen(8080);

Responding with no content

The server can choose to respond with a "No Content" status code 204.

In that case, Sircl will not update the target of the page part request, hence the page stays unchanged.

Redirections

The HTTP standard foresees the 3xx status codes for redirects. These redirects are handled by the browser and cannot be "seen" by Sircl.

To instruct a redirect that can be handled by Sircl, return a regular status code 200 or 204, and include a Location header with the URL of the new location to retrieve.

Location: /AnotherPagePart

Back navigation

The server can also instruct the browser to navigate back using the X-Sircl-History response header with either of these values:

  • back : return back and use cached data of the page if available
  • back-uncached : return back but without using any cached data

The back navigation instruction is typically sent with a 200 or 204 status code.

If the server response has the X-Sircl-History-AllowClose header set, then the back navigation will result in closing the current browser tab/window if it is the only page in the browsers history. If the X-Sircl-History-AllowClose header has value "true", the browser tab will close without further notification. Any other value of the header will be used as confirm message and will allow the user to accept or reject closing the tab.
The X-Sircl-History-AllowClose header is the equivalent of the onback-allowclose class/attribute on "history:back" links.

Changing the target

The server can request to change the target of the response. It does so by specifying a new target with the X-Sircl-Target header. The target should be a CSS selector (extended selector are supported and are relative to the trigger of the request) of the new target.

Take for instance the following HTML fragment:

<a href="/Update" target="#t1">Click me !</a>
<div id="t1">T1</div>
<div id="t2">T2</div>

Clicking the link is expected to update the t1 element.

However, the server can include following response header with its response:

X-Sircl-Target: #t2

In that case, the response will replace the content of the t2 element instead.

In addition to CSS selectors matching the new target, two special values for the X-Sircl-Target header are supported:

  • "main": in Single-Page mode, the new target should be the default target.

  • "_self": though a page part may have been requested, the response is a full page.
    Note that in this case, the request must be of method GET and will be reissued a second time, by the browser, to load the full document.

Rendering an alert

With it's response to the request, the server can set a X-Sircl-Alert-Message header. The value of the header will then be used to render an alert box.

 

 

 

 

 

Loading...