Bobulous Central → Coding → Paging with XSLT 2.0.
This page describes a way of using XSLT 2.0 to generate ordered HTML pages from a single XML file, with links to the previous and next pages appearing when appropriate.
For instance, you might have a single XML file that describes all of the hotels in a town, and want to produce HTML pages that list these hotels, with at most ten hotels appearing on a single page. Or you might use an XML file to catalogue the articles on your website, and want to use that file to generate a set of contents pages, such that no more than twenty-five article links are listed on each contents page.
This page assumes you already have some experience in working with XSLT.
Note that this article is about XSLT 2.0. I know of no way to generate multiple pages in XSLT 1.0, so you must be using an XSLT 2.0 processor as XSLT 2.0 offers many more advanced features than the older standard, including the result-document and for-each-group elements.
An excellent, open source XSLT 2.0 processor is Saxon-HE (home edition) which can be run on Java on any platform. It runs on the command line, so you need to read the documentation to learn how to invoke it properly to process your XML and XSLT files.
The item elements here represent whatever it is you are trying to list across multiple pages, so each item element might be a hotel or an article from your site. The fact that this example structure is simple is unimportant. The important point is that using XSLT you can target the item elements. What goes in the item elements can be as complex as you need for your own purposes.
To produce pages of results from one XML file, we need to decide how many items can appear on each page, then gather our item elements into groups containing at most that quantity, then produce one output file for each of these groups. Breaking this into steps . . .
The first thing that needs to appear in the XSLT stylesheet is a set of variables, one of which defines how many items we want to appear on each result page. After these variables I also define an output format for XHTML, but you should use whatever output format suits your needs, be it XHTML, HTML or XML.
Now you can simply change the value of the itemsPerPage variable in one place if you want to vary how many items are allowed to be listed on each result page.
We'll get to the processing of each group in the next section, but for now let's look at the for-each-group element in detail. Its select attribute is an XPath 2.0 expression which calculates a page number for every item element in your XML file. Here is a worked example of the XPath expression using a value of 4 for $itemsPerPage.
The above table shows how the page number for each item element is calculated. The first column shows the item number (which is the value of the XPath position function). The second column shows the result of dividing this by four (our example value for the items per page variable). And the third column shows the result of rounding this value up using the XPath ceiling function, which gives the page number to which the item belongs.
What happens next is that for-each-group gathers the item elements into groups (or pages in our case) based on these page numbers. So all the item elements with a calculated page number of 1 are found in group 1, and so on.
XSLT 2.0 also introduced the result-document element, which allows your template to create separate output documents with names constructed from XML data and XPath expressions. This allows us to produce our item listing pages, each page having its own name.
This simply creates a filename for the output document for the current page. I think it's wise to keep the first page named index.html so this block checks whether the current group number (which is returned by the XPath position() function) is equal to one, and sets the $pageName variable to "index.html" if so. Otherwise the value of $pageName is set to "index_p2.html" for page two, "index_p3.html" for page three, and so on.
Note that the format="XHTML" attribute refers to the format defined by the xsl:output element which we defined at the top of the XSLT file. I use XHTML, but you need to use an output format suitable for your needs.
In this case, we're calling a specific template (which basically just dumps out the text values found in the item), but you can put whatever you like in the for-each element to generate markup for each item.
That's the basic structure needed for producing multiple output pages based on a single XML file. Next up, it's nice to add "previous" and "next" links to each page.
We need to pass to the template the current page number (given by position(), which returns the current group number) and also the total number of pages (our $pagesNeeded variable created at the top of the XSLT file), because the template won't have access to these values otherwise.
<!-- Add a little spacer symbol between the "next"
This checks the current page number and the determines whether a "previous page" link is needed (if page number is greater than one), and whether a "next page" link is needed (if page number is less than the total number of pages). It also determines whether a spacer is needed (when both links appear together), and the names of the pages to which it is linking. You'll need to change this code if your page names follow a different pattern than "index.html", "index_p2.html" and so on.