Partial Page Loading is a means to reduce the amount of data sent by the server to the client when navigating from one page to another. The new content is requested via AJAX.
Partial Page Loading is enabled with the following entry in conf/config.xml
:
<config>
<acceleration>
<partial-page-loading/>
</acceleration>
</config>
We strongly recommend registering ACs unconditionally if PPL is enabled.
Links (a
elements) are not followed with Partial Page Loading
ai-ppl="false"
, orai.urls.isExternal()
.Forms (form
elements) are not submitted with Partial Page Loading
ai-ppl="false"
,action
is considered external according to ai.urls.isExternal()
,enctype
is multipart/formdata
and the Browser does not support the JavaScript FormData
objectDuring the transition from one page to another, the child elements of the head
element are replaced except for the following child elements: script
, style
, iframe
and stylesheet link
.
script
elements, style
elements and stylesheet link
elements are only added if they have an ai-load
attribute with the value true
. If they have the same id
attribute value as an element in the old head
, the old element is replaced by the new one. Otherwise the new element is appended to the head
.
noscript
elements if they are dynamically inserted, therefore all noscript
elements are removed from PPL responses. ai-ppl
attributeThe ai-ppl
attribute can be used on a
and form
elements. Defined values are true
(the default) and false
. ai-ppl="false"
prevents PPL to follow the link or submit the form.
ai-ppl="true"
does not cause links to be followed or forms to be submitted via PPL when partial-page-loading
is not enabled in config.xml. ai-ppl
attribute appears as data-ai-ppl
in the client-side DOM. ai-load
attributeThe ai-load
attribute can be used on script
, style
and stylesheet link
elements. Defined values are true
and false
(the default). An element with ai-load="true"
is added to the head
element.
ai-load
attribute appears as data-ai-load
in the client-side DOM. When a site consists of pages sharing content items (e.g. headers or footers), Partial Page Loading with Fragments can be used to further reduce the content sent by the server.
Example:
<body>
<header>
... The header is the same on all pages ...
</header>
<ai-fragment id="article" provides="article_0">
<article>
... The article content differs from page to page ...
</article>
</ai-fragment>
<footer>
... The footer is the same on all pages ...
</footer>
</body>
ai-fragment
elementbody
elementid
(required)provides
(optional)The ai-fragment
element wraps content that has to be replaced while navigating from one page to another in a container.
The id
attribute identifies the fragment container. In addition to the restrictions for id attribute values from the HHTML5 specification, the value must not contain any EQUALS SIGN (‘=’) or SOLIDUS (‘/‘) characters.
The provides
attribute identifies the content of the fragment container (without nested fragment containers). If the attribute is present its value must not be empty and must not contain any space, EQUALS SIGN (‘=’) or SOLIDUS (‘/‘) characters. If the provides
attribute value for a fragment on two pages is the same, the fragment’s content will not be replaced; however, content of nested fragments may be replaced.
Start and end tags of the ai-fragment
element are replaced by comments on the server. Therefore the two p
elements in
...
<p id="outside">outside a fragment</p>
<ai-fragment id="myId" provides="myProvides">
<p id="inside">inside a fragment</p>
</ai-fragment>
...
become siblings in the client DOM:
...
<p id="outside">outside a fragment</p>
<!--ai-fragment myId=myProvides-->
<p id="inside">inside a fragment</p>
<!--ai-fragment-end-->
...
When partial page loading is not enabled, ai-fragment
start and end tags are removed.
page 1:
<body>
...
<p>This text is the same on all pages; it is not within an ai-fragment element.</p>
...
<ai-fragment id="outer" provides="outer_1">
<p>Some text in outer</p>
</ai-fragment>
...
</body>
page 2:
<body>
...
<p>This text is the same on all pages; it is not within an ai-fragment element.</p>
...
<ai-fragment id="outer" provides="outer_2">
<h2>A headline</h2>
<p>This is some other text in the 'outer' fragment.</p>
</ai-fragment>
...
</body>
When navigating from page 1 to page 2, only the content of fragment ‘outer’ is replaced.
page 1:
<body>
...
<p>This text is the same on all pages; it is not within an ai-fragment element.</p>
...
<ai-fragment id="outer" provides="outer_1">
<p>Some text in outer</p>
<ai-fragment id="inner1" provides="p1_1">
<p>Inner 1 - 1</p>
</ai-fragment>
<ai-fragment id="inner2" provides="p2_1">
<p>Inner 2 - 1</p>
</ai-fragment>
</ai-fragment>
...
</body>
page 2:
<body>
...
<p>This text is the same on all pages; it is not within an ai-fragment element.</p>
...
<ai-fragment id="outer" provides="outer_1">
<p>Some text in outer</p>
<ai-fragment id="inner1" provides="p1_2">
<p>Inner 1 - 2</p>
</ai-fragment>
<ai-fragment id="inner2" provides="p2_2">
<p>Inner 2 - 2</p>
</ai-fragment>
</ai-fragment>
...
</body>
When navigating from page 1 to page 2, only the content of fragments ‘inner1’ and ‘inner2’ is replaced, because the values of their provides
attributes differ. Since the ‘outer’ fragment’s provides
attribute values on both pages are the same, this fragment’s content is not updated.
ai.ppl
is no safe indicator for partial-page-loading
being enabled. ai.ppl.followLink
This function starts a Partial Page Loading transition to a document with the given URL.
Signature:
ai.ppl.followLink(url)
Arguments:
Returns {boolean} true
if the link was followed via Partial Page Loading, false
otherwise.
ai.ppl.submitForm
This function submits the given form via Partial Page Loading.
Signature:
ai.ppl.submitForm(formElement)
Arguments:
form
element representing the form to submit.Returns {boolean} true
if the form was submitted via Partial Page Loading, false
otherwise.
pplready
and pplinit
The pplready
event is triggered immediately when the Partial Page Loading session is initialized, even before the load event.
The data parameter passed to listeners is of type ai.ppl.ReadyData
. Properties:
pplready
in any way may cause a JavaScript error and the site may fail to load. The pplinit
event is triggered when the Partial Page Loading session is initialized and the DOM is ready (load
event dependent).
The data parameter supplied for listeners is of type ai.ppl.InitData
. Properties:
Both, the pplready
and the pplinit
event are triggerOnRegister
event types.
pplnavstart
This event is triggered when the transition from one document to another starts. The data parameter passed to listeners is of type ai.ppl.NavStartData
.
Properties:
GET
or POST
).ai.ppl.followLink()
.For backwards-compatibility, PPL always fires a pplstart
event together with a pplnavstart
event. The data passed to listeners for pplstart
and pplnavstart
are the same.
pplcontentready
This event is triggered when the content of the new document is available. The data parameter passed to listeners is of type ai.ppl.ContentReadyData
.
Properties:
body
element or the top-level nodes of the old fragment (depending on the value of withFragments
).body
element or the top-level nodes of the new fragment (depending on the value of withFragments
).true
if the fragment content is involved, false
if the body
element is involved.pplnavend
This event is triggered when the transition from one document to another is finished. The data parameter passed to listeners is of type ai.ppl.NavEndData
. The event data object contains everything the ai.ppl.EndData
object contains.
Properties:
For backwards-compatibility, PPL always fires a pplend
event together with a pplnavend
event. The data passed to listeners for pplend
and pplnavend
are the same.
The following code shows a simple load overlay when transitioning from one document to another. This is just an example showing how PPL events can be used. It is not meant to be code ready to be used as shown in every circumstance.
CSS:
.loading { color: #000; background: #ddd; opacity: 0.7; width: 100%; height: 100%; position: absolute; top: 0; left: 0; }
.loading h1 { text-align: center; margin-top: 30%; }
JS:
(function() {
var
/** @type Element */
_load = null
/** @type Element */
, _body = null
;
function _createLoad() {
var
/** @type Element */
load = document.createElement('div')
/** @type Element */
, h = document.createElement('h1')
/** @type Text */
, loadText = document.createTextNode('Loading...')
;
h.appendChild(loadText);
load.setAttribute('class', 'loading');
load.appendChild(h);
return load;
}
/** * Handler for pplready. * * @param {ai.ppl.ReadyData} data The Partial Page Loading ready event data. */
function _pplreadyHandler(data) {
ai.addEventListener('pplnavstart', _pplnavstartHandler);
ai.addEventListener('pplcontentready', _pplcontentreadyHandler);
ai.addEventListener('pplnavend', _pplnavendHandler);
}
/** * Handler for pplinit. * * @param {ai.ppl.InitData} data The Partial Page Loading init event data. */
function _pplinitHandler(data) {
_load = _createLoad();
_body = document.body;
}
/** * Handler for pplnavstart. * * @param {ai.ppl.NavStartData} data The Partial Page Loading navstart event data. */
function _pplnavstartHandler(data) {
_body.appendChild(_load);
}
/** * Removes the old body attributes. * * @param {Element} oldBody The old body element. */
function _removeBodyAttributes(oldBody) {
var
/* @type NamedNodeMap */
attrs = oldBody.attributes
/* @type number */
, l = attrs.length
/* @type number */
, i = 0
;
for (i = 0; i < l; i++) {
oldBody.removeAttribute(attrs.item(i));
}
}
/** * Sets the new body attributes. * * @param {Element} oldBody The old body element. * @param {Element} newBody The new body element. */
function _setNewBodyAttributes(oldBody, newBody) {
var
/* @type NamedNodeMap */
attrs = newBody.attributes
/* @type number */
, l = attrs.length
/* @type number */
, i = 0
/* @type Attr */
, attr = null
;
for (i = 0; i < l; i++) {
attr = attrs.item(i);
oldBody.setAttribute(attr.name, attr.value);
}
}
/** * Handler for pplcontentready. * * @param {ai.ppl.ContentReadyData} data The Partial Page Loading contentready event data. */
function _pplcontentreadyHandler(data) {
var
/* @type Element */
newNode = data.newNodes[0]
/* @type Element */
, oldNodes = data.oldNodes[0]
/* @type DocumentFragment */
, df = null
;
if (!data.withFragments) {
/* no fragments */
_removeBodyAttributes(oldNode);
_setNewBodyAttributes(oldNode, newNode);
df = document.createDocumentFragment();
while (newNode.hasChildNodes()) {
df.appendChild(newNode.firstChild);
}
while (oldNode.hasChildNodes() && oldNode.firstChild !== _load) {
oldNode.removeChild(oldNode.firstChild);
}
oldNode.insertBefore(df, _load);
data.preventDefault();
}
}
/** * Handler for pplnavend. * * @param {ai.ppl.NavEndData} data The Partial Page Loading end event data. */
function _pplnavendHandler(data) {
_body.removeChild(_load);
}
ai.addEventListener('pplready', _pplreadyHandler);
ai.addEventListener('pplinit', _pplinitHandler);
})();