Bootstrap support

Bootstrap is a popular open source front-end toolkit. The Sircl Bootstrap extension complements Bootstrap by adding Javascript-less support for common usages of some more complex controls as tab pages, modals, offcanvasses and toasts.

Introduction

Bootstrap is a popular open source front-end toolkit defining a responsive grid system and an extensive collection of styled and interactive components.

For more about Bootstrap, please see https://getbootstrap.com/.

Sircl extends and complements Bootstrap by offering the Sircl capabilities such as page part loading on interactive Bootstrap components.

Setup

Sircl provides extensions for Bootstrap through its extension libraries sircl-bootstrap3.js, sircl-bootstrap4.js and sirc.bootstrap5.js. Use the first if your site is using Bootstrap 3.x, the second if your site uses Bootstrap 4.x and the latter if your site uses Bootstrap 5.x.

Bootstrap itself should be installed before the Sircl extensions for Bootstrap: the reference to the Bootstrap scripts should appear before the reference to the Sircl Bootstrap extension script in your sourcefile.

See the Get Started section for further installation instructions and Bootstrap starter templates.

Overrides

Simply by being installed, the Sircl extension library for Bootstrap overrides some behaviour of Sircl:

Spinners

When the Sircl Bootstrap library for Bootstrap 4+ is installed, it automatically overwrites the default Sircl spinner with a Bootstrap spinner component:

Note: Later Sircl scripts or custom scripts can overwrite the spinner again by assigning the HTML string for the spinner to the sircl.html_spinner variable. Note that the spinner must have the sircl-spinner class to operate correctly with Sircl.

Load Progression

The Sircl extension to Bootstrap extends the Load Progression capabilities of Sircl explained in the section about Partial Loading, to support Bootstrap Progress (bars) as well with the upload-progress and download-progress classes.

<a href="/Reload" target="#t1">Click me !</a>
<div id="t1" download-progress="#p1">
  Click the link to reload this data.
</div>
<div class="progress" id="p1" hidden>
  <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
</div>

Collapse

A Bootstrap collapse creates a region to show and hide content similar to expanding and collapsing a region.

Loading on expanding

Collapsable regions that have an ifexpanded-load attribute containing an URL, will have their content loaded from that URL when expanded.

For example (using Bootstrap 5 notation):

<div>
  <a class="btn btn-primary" data-bs-toggle="collapse" href="#cdetails">Details</a>
</div>

<div class="collapse" id="cdetails" ifexpanded-load="/Products/55/Details">
  Loading...
</div>

Connect to checkboxes or radio buttons

Besides using a button or link to expand or collapse a Bootstrap collapse region using standard Bootstrap features, you can expand or collapse a Bootstrap collapse region by checking/unchecking a checkbox or radio button.

Adding an ifchecked-expand attribute to a checkbox, with as value a (relative) CSS selector to a collapse region, will expand the region when the checkbox is checked, and collapse the region when the checkbox is unchecked. On page (part) load, the regions will be expanded or collapsed to match the initial state of the checkboxes.

When used on radio buttons, the ifchecked-expand attribute will ensure only one collapse region is expanded at a time, according to the operation of radio buttons.

For example:

<div>
  <label><input type="radio" name="Choice" value="1" ifchecked-expand="#info1"/> Option one</label>
</div>
<div class="collapse" id="info1">
  Info about option 1
</div>
<div>
  <label><input type="radio" name="Choice" value="2" ifchecked-expand="#info2"/> Option two</label>
</div>
<div class="collapse" id="info2">
  Info about option 2
</div>

Requires Sircl 2.1.1+

Saving state

If you want the server-side of your application to "remember" which regions were collapsed and expanded, you can have a (hidden) field informed about the state of a collapse region by adding an onexpand-set attribute on the collapse region, with as value the name of an INPUT form element to store the state in. The value of that control is set to "true" when the region is expanded, and "false" when the region is collapsed.

For example (using Bootstrap 5 notation):

<div>
  <a class="btn btn-primary" data-bs-toggle="collapse" href="#cdetails1">Details</a>
</div>
<div class="collapse" id="cdetails1" onexpand-set="cdetails1state">
  Details 1
</div>
<input type="hidden" name="cdetails1state" />

Note that using connected checkboxes or radio buttons is an alternative way to store the state of collapse regions, as that state is represented by the state of the checkboxes or radio buttons, and is sent to the server on submitting the surrounding form.

Requires Sircl 2.1.1+

Modal windows

The Bootstrap modal plugin offers support for "modal windows" within your web page, quite similar to HTML dialogs.

Therefore, the support Sircl provides for Bootstrap modals is very similar to it's support for HTML dialogs.

Showing modals after delay

Bootstrap provides support to show modal windows on button click. Sircl adds the ability to open the modal window immediately or some delay after the page (part) is loaded by means of the onload-showmodalafter attribute which takes as value the number of seconds to delay the opening of the window.

An example:

<div class="modal fade" tabindex="-1" onload-showmodalafter="5">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-body">
        <h5>Welcome!</h5>
      </div>
    </div>
  </div>
</div>

Note that because Bootstrap only supports one modal window at a time, the modal will only show if no other Bootstrap modals are shown.

Loading content on show

Whenever a modal window having a onshowmodal-load attribute (containing an URL) on a child element of the .modal element is shown, the URL is invoked and the response is injected in the element holding the attribute.

For example (using Bootstrap 5 notation):

<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#registerModal">
  Register
</button>

<div class="modal fade" id="registerModal" tabindex="-1">
  <div class="modal-dialog">
    <div class="modal-content" onshowmodal-load="/RegistrationForm.html">
      <div class="modal-header">
        <h5 class="modal-title">Register</h5>
      </div>
      <div class="modal-body">
        Loading registration form...
      </div>
    </div>
  </div>
</div>

In the above example, when the Register button is pressed, the modal window is shown. And since one of its elements contains an onshowmodal-load attribute, that element will have its content replaced by the response of the URL request.

The onshowmodal-load action is repeated every time the modal is shown unless the element holding the onshowmodal-load attribute has the noreload class. Or you can use the onclose-restore class as explained below, to restore the content of the modal between different times the modal window is shown.

Showing modals from load

Whenever content is loaded inside a .modal (an element with the "modal" class), the modal window will automatically show up.

For example:

<a href="/NewsletterInvite" target="#nlimdl"> Subscribe to our newsletter </a>

<div id="nlimdl" class="modal fade" tabindex="-1">
</div>

Clicking the hyperlink will load the /NewsletterInvite page part. Because the inline target matches the .modal element, or matches any element inside a .modal element, when the content of the page part is loaded and injected in the target element, the surrounding modal window will show up automatically.

The /NewsletterInvite page part is assumed to have a .modal-dialog root element containing a .modal-content element to be Bootstrap compliant. Remember the target of the hyperlink triggering the page part load can be the .modal element, but also any element inside. Depending on where the target has been placed, the content of the response to the /NewsletterInvite will vary.

Note that because Bootstrap only supports one modal window at a time, Sircl will automatically hide other modals when showing a new modal due to content load.

Preshow modals and restore content

In the above example, showing modals from load, the modal window shows up once the load request is terminated and the response is known. There is a good reason for that: the server could still decide to return content for another inline target by means of the X-Sircl-Target response header (see the Partial Loading section), or to return no content at all (returning status code 204).

If the target .modal element (the .modal element that is the target or that surrounds the target of a request) has the class beforeload-showmodal, then the modal window will show up before initiating the server request:

<a href="/NewsletterInvite" target="#nlimdldlg"> Subscribe to our newsletter </a>

<div class="modal fade beforeload-showmodal onclose-restore" tabindex="-1">
    <div class="modal-dialog" id="nlimdldlg">
        <div class="modal-content">
            <div class="modal-body">
                <p>Loading...</p>
            </div>
        </div>
    </div>
</div>

The modal window typically shows a loading message, spinner or animation that will be replaced by the loaded content.

Since the beforeload-showmodal class causes the modal to show before the content is loaded, there should already be .modal-dialog and .modal-content elements, and the .modal-dialog element should not be replaced. Therefore, the target of the page part load should not be the .modal element itself, but the .modal-dialog element, or any element inside it. Otherwise, Bootstrap cannot properly handle the modal.

Depending on the response given by the server, different actions are taken:

  • The server returns content as expected: the content of the target is replaced with the content returned from the server.
  • The server returns content but also a X-Sircl-Target header to retarget the content: the modal will be closed, and the content rendered in its new target. If that new target is part of another modal window, that window will show up.
  • The server returns no content (status code 204): the modal window will be closed.

In the default scenario, the modal window stays visible and the target content is replaced.

When the modal window is closed later on, and maybe reopened later, the target still contains the content of the previous load. Adding an onclose-restore class to the .modal element ensures that it's original content (i.e. a "loading..." message) is restored in the window once it is closed, so that if the window shows up a second time, it still initially has its initial content (i.e. a "loading..." message).

Close modals from load

Whenever content is loaded inside a .modal (an element with the "modal" class), the modal window will automatically show up.

In the same way, when a trigger inside a .modal causes content to be loaded outside the .modal, the modal window will automatically close.

<div id="welcoming">
  <div class="overlay" hidden></div>
  Hello Kim !
  <button type="button" data-bs-toggle="modal" data-bs-target="#editmdl" class="btn btn-primary">
    Edit
  </button>
</div>
<div class="modal" id="editmdl" tabindex="-1">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-body">
        <form action="/UpdateName" target="#welcoming">
          <input type="text" name="name" />
	      <button type="submit"> OK </button>
        </form>
      </div>
    </div>
  </div>
</div>

Close modals from response status

When a load action takes place and the target is a .modal element, or is inside a .modal element, and the server responds with a status code 204 (No content) to the load action, then that modal window will be closed.

In the following example:

<div class="modal fade" tabindex="-1">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-body">
        <form action="/Register" class="target">
          <input type="text" name="Name" placeholder="Name"/><br/>
          <button type="submit"> Register </button>
        </form>
      </div>
    </div>
  </div>
</div>

If, when submitting the form, the server responds with a status code 204, given the target of the request is the form itself, the modal window containing the form will be closed.

Offcanvas

The Bootstrap Offcanvas component is new to Bootstrap 5 and allows to build hidden sidebars.

Loading content on show

Whenever a sidebar having a onshowoffcanvas-load attribute (containing an URL) on a child element of the .offcanvas element is shown, the URL is invoked and the response is injected in the element holding the attribute.

For example (using Bootstrap 5 notation):

<button type="button" class="btn btn-primary" data-bs-toggle="offcanvas" data-bs-target="#registerCnv">
  Register
</button>

<div class="offcanvas offcanvas-start" id="registerCnv" onshowoffcanvas-load="/RegistrationForm.html" tabindex="-1">
  <div class="offcanvas-header">
    <h5 class="offcanvas-title">Register</h5>
  </div>
  <div class="offcanvas-body">
    Loading registration form...
  </div>
</div>

In the above example, when the Register button is pressed, the offcanvas sidebar is shown. And since one of its elements contains an onshowoffcanvas-load attribute, that element will have its content replaced by the response of the URL request.

The onshowoffcanvas-load action is repeated every time the sidebar is shown. You can use the onclose-restore class as explained below, to restore the content of the sidebar between different times the sidebar is shown.

Showing offcanvas from load

Whenever content is loaded inside an .offcanvas (an element with the "offcanvas" class), the sidebar will automatically show up.

For example:

<a href="/NewsletterInvite" target="#nlicnv"> Subscribe to our newsletter </a>

<div id="nlicnv" class="offcanvas offcanvas-start" tabindex="-1">
</div>

Clicking the hyperlink will load the /NewsletterInvite page part. Because the inline target matches the .offcanvas element, or matches any element inside an .offcanvas element, when the content of the page part is loaded and injected in the target element, the surrounding offcanvas sidebar will show up automatically.

Note that because Bootstrap only supports one offcanvas at a time, Sircl will automatically hide other sidebars when showing a new sidebar due to content load.

Preshow offcanvas and restore content

In the above example, showing offcanvas from load, the sidebar shows up once the load request is terminated and the response is known. There is a good reason for that: the server could still decide to return content for another inline target by means of the X-Sircl-Target response header (see the Partial Loading section), or to return no content at all (returning status code 204).

If the target .offcanvas element (the .offcanvas element that is the target or that surrounds the target of a request) has the class beforeload-showoffcanvas, then the sidebar will show up before initiating the server request:

<a href="/NewsletterInvite" target="#nlicnv2"> Subscribe to our newsletter </a>

<div id="nlicnv2" class="offcanvas offcanvas-start beforeload-showoffcanvas onclose-restore" tabindex="-1">
  <div class="offcanvas-body">
    <p>Loading...</p>
  </div>
</div>

The sidebar typically shows a loading message, spinner or animation that will be replaced by the loaded content.

Depending on the response given by the server, different actions are taken:

  • The server returns content as expected: the content of the target is replaced with the content returned from the server.
  • The server returns content but also a X-Sircl-Target header to retarget the content: the sidebar will be closed, and the content rendered in its new target. If that new target is part of another offcanvas sidebar, that sidebar will show up.
  • The server returns no content (status code 204): the sidebar will be closed.

In the default scenario, the sidebar stays visible and the target content is replaced.

When the sidebar is closed later on, and maybe reopened later, the target still contains the content of the previous load. Adding an onclose-restore class to the .offcanvas element ensures that it's original content (i.e. a "loading..." message) is restored in the offcanvas sidebar once it is closed, so that if the sidebar shows up a second time, it still initially has its initial content (i.e. a "loading..." message).

Close offcanvas from load

Whenever content is loaded inside an .offcanvas (an element with the "offcanvas" class), the sidebar will automatically show up.

In the same way, when a trigger inside an .offcanvas causes content to be loaded outside the .offcanvas, the offcanvas sidebar will automatically close.

Close offcanvas from response status

When a load action takes place and the target is an .offcanvas element, or is inside an .offcanvas element, and the server responds with a status code 204 (No content) to the load action, then that sidebar will be closed.

In the following example:

<div class="offcanvas offcanvas-start" tabindex="-1">
  <div class="offcanvas-body">
    <form action="/Register" class="target">
      <input type="text" name="Name" placeholder="Name"/><br/>
      <button type="submit"> Register </button>
    </form>
  </div>
</div>

If, when submitting the form, the server responds with a status code 204, given the target of the request is the form itself, the sidebar containing the form will be closed.

Tabs & Pills

Boostrap tabs and pills are nav(igation) components that allow navigating to pages or to tab pages.

Sircl's support for tabs and pills is mainly around the use of (dynamically loading) tab pages.

Dynamically loading tabs

Bootstrap makes it possible to navigate through tab panes by means of tabs or pills, by using data attributes instead of Javascript.

To this, Sircl adds the ability to dynamically load the content of a tab pane whenever it is shown.

When the ifactivetab-load attribute is placed on a .tab-pane element, and the attribute contains a valid URL to be loaded, the content retrieved requesting that URL will be loaded in the tab pane when it becomes active. The dynamic load will also occures on the initially active tab pane.

For example (using Bootstrap 5 notation):

<ul class="nav nav-tabs">
    <li class="nav-item">
        <button class="nav-link active" data-bs-toggle="tab" data-bs-target="#intro" type="button">Intro</button>
    </li>
    <li class="nav-item">
        <button class="nav-link" data-bs-toggle="tab" data-bs-target="#details" type="button">Details</button>
    </li>
    <li class="nav-item">
        <button class="nav-link" data-bs-toggle="tab" data-bs-target="#pics" type="button">Pictures</button>
    </li>
</ul>

<div class="tab-content">
    <div class="tab-pane active" id="intro" ifactivetab-load="/IntroContent">Loading...</div>
    <div class="tab-pane" id="details" ifactivetab-load="/DetailsContent">Loading...</div>
    <div class="tab-pane" id="pics" ifactivetab-load="/PicsContent">Loading...</div>
</div>

Routing to tabs

By default, Bootstrap tabs or pills can load arbitrary URLs, or can activate a tab pane. In the latter case, there is no default way to navigate directly to a specific tab in Bootstrap.

Sircl offers this facility, called routing to tabs, by surrounding the tabs (or pills) with the .hash-routed class.

When the class is set (typically on the .nav element), when switching tab, the URL of the browser is extended with a hash expression refering to the selected tab.

Reversly, when navigating to a page managed by Sircl, with a hash expression in the URL, Sircl will attempt to map the hash expression to a tab or pill inside a .hash-routed context, and if found, activate that tab/pill.

Example:

<ul class="nav nav-tabs hash-routed">
    <li class="nav-item">
        <a class="nav-link active" data-bs-toggle="tab" href="#intro">Intro</a>
    </li>
    <li class="nav-item">
        <a class="nav-link" data-bs-toggle="tab" href="#details">Details</a>
    </li>
    <li class="nav-item">
        <a class="nav-link" data-bs-toggle="tab" href="#pics">Pictures</a>
    </li>
</ul>

<div class="tab-content">
    <div class="tab-pane active" id="intro">Intro...</div>
    <div class="tab-pane" id="details">Details...</div>
    <div class="tab-pane" id="pics">Pictures...</div>
</div>

By default, routing to tabs will cause the URL in the browser to be updated to contain the reference to the active tab, but no new history entry is pushed on the browser stack. This means that if you switch from tabs, the hash expression in the URL will change, but when you press the Back button of the browser, it is not the previous tab that will be activated, but the previous page that will be loaded.

To change this behaviour and have routing to tabs to push new history entries on the browser stack, add the history="push" attribute on the element having the hash-routed class.

Toasts

Bootstrap toasts are available since Bootstrap 4 and offer push-notification like alert messages.

Showing Toasts after load

Pages and page parts can contain Bootstrap toasts. To have them show automatically when the page or page part is loaded, add the onload-showtoast class on them:

<div class="position-fixed bottom-0 end-0 p-3" style="z-index: 11">
  <div id="liveToast" class="toast onload-showtoast">
    <div class="toast-header">
      <strong>Sample Toast</strong>
    </div>
    <div class="toast-body">
      This is a sample toast message.
    </div>
  </div>
</div>

Active route

When using Sircl and loading page parts, often nav items or navbar items are outside the loaded page part but need to be updated to reflect a new active item.

Especially when using Sircl in Single Page mode, the page content and the browser URL changes but if the surrounding menus are not included in the main target, they are not updated and do not reflect the page/URL change. To update the menu's active elements by setting the active class only on the (navigation) items that reflect the currently active page based on the browsers URL, you can use the ifroute-setactive attribute which takes a regular expression that is matched towards the path of the browers URL.

Consider the following Bootstrap menu:

<nav class="navbar navbar-expand-lg navbar-light bg-light">
  <div class="container-fluid">
    <div class="collapse navbar-collapse">
      <div class="navbar-nav">
        <a class="nav-link" href="/" ifroute-setactive="^\/$">Home</a>
        <a class="nav-link" href="/Features" ifroute-setactive="^\/Features(\/|$)">Features</a>
        <a class="nav-link" href="/Pricing" ifroute-setactive="^\/Pricing(\/|$)">Pricing</a>
      </div>
    </div>
  </div>
</nav>

The "Home" link will me receive the active class if the path part of the page URL matches the regular expression ^\/$, which basically means the path must be a simple slash, nothing before nor behind it. If the regular expression does not match, the active class is removed from the element (if it has it).

The "Features" link will be made active if the path part matches the regular expression ^\/Features(\/|$), which means the path must begin with "/Features" and either contain nothing else, or be followed by a slash (whereafter more can follow). Following paths would match this regular expression and make the Features link active:

  • /Features
  • /Features/
  • /Features/First
  • /Features/23/Details

But the following path is not a match:

  • /FeaturesAndServices

The "Pricing" link is matched similarly.

The ifroute-setactive attribute matches regular expression in a case-insensitive manner.

 

Loading...