-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathXSLT for the modern web - Part 1.html
1412 lines (1067 loc) · 50.3 KB
/
XSLT for the modern web - Part 1.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
<title>XSLT for the modern web - Part 1</title>
</head>
<body>
<main>
<section id="part-1">
<div class="container">
<h1>XSLT for the modern web</h1>
<h2 id="context">Context</h2>
<p>XSL (eXtensible Stylesheet Language) is a XML-based stylesheet language to present and extract fragments of XML documents. XSLT is defined as XSL with a Transformation ability (i.e XSL + T = XSLT).</p>
<p>XSLT is generally used to extract and transform fragments of one XML document into another XML vocabularly or to pick out selected context to make a HTML output. It can also be used more creatively to transform fragments of XML in a non-XML based syntax, such as CSV or plain text.</p>
<p>I encountered basic XSLT during a MA Digital Humanities programme. The XSLT course provided a summary look at XSLT and how it could be used from the perspective of basic humanities texts, encoded using the <a href="https://tei-c.org/support/learn/introducing-the-guidelines/" title="Introducing the Guidelines – TEI: Text Encoding Initiative">Text Encoding Initiative (TEI) markup vocabulary</a><p>
<p>Ten years plus later, while my general memory of the programme has started to fade, the XSLT course of the programme really interested me and I enjoyed learning XSLT.</p>
<p>The original <a href="https://www.w3.org/TR/1999/REC-xslt-19991116" title="XSL Transformations (XSLT) Version 1.0 W3C Recommendation 16 November 1999">XSL Transformations (XSLT) Version 1.0</a>XSLT specification is now over 20 years old, however pieces of software were built around XSLT is still a relevent technology, though not one that will generate a huge following. It is worthwhile being reflective on how some aspects of web technology can make an active resurgance at a later date - the humble .gif image format which once was used to excess on 90's Myspace profiles, has since become relevent since the dawn of social media and internet memes, equally Javascript was also once considered a 'toy' language and early uses were widely seen as gimmic or a source of irritation, but now it has developed into one of the top programming languages to learn and is used for both simple personal websites and powering large-scale application-style websites with millions of users.</p>
<p>The use of XSLT within websites declined due to other tools and simpler formats for data exchange becoming more popular, in particular the JSON (Javascript Object Notation) format, which provides a more concise object notation to exchange data. There are some areas though where XML and XSLT remains relevant to use where texts are involved or the source material used is by default encoded in, or lends itself to, an XML first format - a key area is humanities computing.</p>
<p>It is worthwhile considering that when the use of XSLT on the web declined, the landscape of the internet was very different that in it now.</p>
<ol>
<li><b>Faster internet connections were not available</b> - the 'overhead' of downloading an XML file of a few MB in filesize could have been a blocker to use.</li>
<li><b>Fast computing (CPU) is prevalent and memory available within web browsers</b> - it is now possible to run entire applications within the web browser, even computationally intensive 3D graphics can be provided within a webpage using newer API's such as WebGL. </li>
<li><b>There is now a strong focus on asynchronous techniques in website development</b> - this is not just due to 'Ajax' techniques (i.e uses of XMLHttpRequest) but more newer features to Javascript such as <code>Promises, await, async</code>. These features make dealing with loading of external resources easier and larger filesize resources much easier.</li>
<li><b>We now have a host of new Web API's that can can be used to support use of XSLT in the browser and in website development</b> - Fetch API, FileReader API, Drag and Drop API, Web Workers, Service Workers, IndexedDB, Web Storage.</li>
</ol>
<p>XSLT is also an interesting language to learn in 2023 since it is template-based and thereby provides a different way to traverse XML documents than via XML DOM or SAX methods and also very different from the scripting languages that dominate the website landscape currently. XSLT will make you <em>think</em> in a different way to scripting languages about how to get the output you want from a transformation. It is also interlaced with an ecosystem other XML related technologies - in particular XSLT would not be possible without XPath which provides a path-based syntax to select specific elements of XML documents.</p>
<h2>Focus</h2>
<p>Briefly, the focus of this text is:</p>
<p>How can XSLT be used within modern browsers of 2023 - Firefox, Google Chrome, Chromium era - and utilising with newer website technology API's, XMLHttpRequest (maybe not so new now in 2023!), Fetch API, FileReader API, Drag and Drop API, Web Workers, Service Workers, IndexedDB, Web Storage.</p>
<p>In places I will use <a href="https://jquery.com/" title="jQuery">jQuery</a> (yes, jQuery is still useful in 2023!) and <a href="https://getbootstrap.com/" title="Get Bootstrap 5.2">Bootstrap 5.2</a></p>
<h2>XSLT Standards</h2>
<p>There are 3 stardard versions of the XSLT recommended specification:</p>
<p><i>XSL Transformations (XSLT) Version 1.0 W3C Recommendation 16 November 1999</i>
<a href="https://www.w3.org/TR/1999/REC-xslt-19991116" title="XSL Transformations (XSLT) Version 1.0 W3C Recommendation 16 November 1999">XSL Transformations (XSLT) Version 1.0 W3C Recommendation 16 November 1999</a></p>
<p><i>XSL Transformations (XSLT) Version 2.0 (Second Edition) W3C Recommendation 30 March 2021 (Amended by W3C)</i>
<a href="https://www.w3.org/TR/2021/REC-xslt20-20210330/" title="XSL Transformations (XSLT) Version 2.0 (Second Edition) W3C Recommendation 30 March 2021 (Amended by W3C)">XSL Transformations (XSLT) Version 2.0 (Second Edition) W3C Recommendation 30 March 2021 (Amended by W3C)</a></p>
<p><i>XSL Transformations (XSLT) Version 3.0 W3C Recommendation 8 June 2017</i>
<a href="https://www.w3.org/TR/2017/REC-xslt-30-20170608/" title="XSL Transformations (XSLT) Version 3.0 W3C Recommendation 8 June 2017">XSL Transformations (XSLT) Version 3.0 W3C Recommendation 8 June 2017</a></p>
<p>If you are using XSLT in 2023 you probably are using it within a legacy application which you need to support or are using XSLT to generate separate files from one XML document via a software application.</p>
<p>When using XSLT in web-browsers it is sensible to assume XSLT 1.0 support is available as a minimum.</p>
<p>Current browsers support XSLT 1.0 with no clear plan to support higher features. XSLT 1.0 will give lots of basic functionality for relatively simple XSLT transformations - but you won't be able to use any features in XSLT 2.0 or 3.0. A summary search engine peruse will likely confirm that browsers are not intending to support anything further than 1.0.</p>
<p><i>Note that there are javascript products that provide XLST support above 1.0, Michael Kay's Saxon products are an example. These products are not included in this text but easily found with a simple websearch.</i></p>
<p>Some interesting Humanities XML files that can be used to experiment with XSLT can be found via the <a href="http://www.perseus.tufts.edu" title="Perseus Digital Library">Perseus Digital Library website</a>.
</p>For this text I am going to be using a extract from <a href="http://www.perseus.tufts.edu/hopper/text?doc=Perseus:text:1999.01.0126" title="Perseus - The Histories, Herodotus">The Histories by Herodotus</a>. Perseus makes this text available under a <a href="http://creativecommons.org/licenses/by-sa/3.0/us/" title="Creative Commons — Attribution-ShareAlike 3.0 United States
— CC BY-SA 3.0 US">Creative Commons Attribution-ShareAlike 3.0 United States License</a>.</p>
<p><i>Note the TEI markup applied to this document is TEI 2. This is now outdated but it cannot be expected that texts are continuously updated as the TEI and for the purpose of this article it does not matter.</i></p>
<h3>XML document (example.xml)</h3>
<p>To investigate modern browsers support for XSLT I used an extract from the Perseus' Digital Library <a href="http://www.perseus.tufts.edu/hopper/text?doc=Perseus:text:1999.01.0126" title="Perseus - The Histories, Herodotus">of The Histories, Herodotus</a> as mentioned above.</p>
<p>The TEI vocabularly is quite verbose, so to make a simpler extract of the content I ended up copying the first five <code><milestone></code> elements within an <code><extract></code> element. Note that TEI markup is often "mixed content", elements are not always enclosing text content.</p>
<pre>
<code>
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="example.xsl"?>
<!-- Text provided by Perseus Digital Library, with funding from The Annenberg CPB/Project. Original version available for viewing and download at http://www.perseus.tufts.edu/hopper/. -->
<extract>
<milestone n="1" unit="section" />
<milestone unit="para" />
Thus the <name type="pers">Mermnadae</name> robbed the <name type="pers">Heraclidae</name> of the sovereignty and took it for themselves.
Having gotten it, <name type="pers">Gyges</name> sent many offerings to <name key="perseus,Delphi" type="place" reg="Delphi [22.5167,38.4917] (Perseus) "><placeName key="perseus,Delphi" authname="perseus,Delphi">Delphi</placeName></name>: there are very many silver offerings of his there; and besides the silver, he dedicated a hoard of gold, among which six golden bowls are the offerings especially worthy of mention.
<milestone...> ... <milestone>
<milestone...> ... <milestone>
<milestone...> ... <milestone>
</extract>
</code>
</pre>
<h2>XSLT document (example.xsl)</h2>
<p>This stylesheet outputs all the text content from the associated XML document between the HTML <code><body></code> tags. As a double check that a transformation is being made, I have included a template statement to match instances of <code>note</code> encountered in the XML document and style the text content italic.</p>
<pre>
<code>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<title>Histories</title>
</head>
<body>
<h1>Histories</h1>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="note">
<i>
<xsl:apply-templates/>
</i>
</xsl:template>
</xsl:stylesheet>
</code>
</pre>
<h3>Browsers</h3>
<p>The examples in this document have been tested on these browsers:</p>
<p>Chromium version 104.0.5112.101 (Official Build) Built on Ubuntu, running on Ubuntu 18.04 (32-bit) and Firefox 103.0 (32-bit)(Mozilla Firefox for Ubuntu, Canonical 1.0)</p>
<p>Note that while Microsoft Edge is based on Chromium, this does not mean these examples should be assumed to work in Edge. As Internet Explorer is no longer supported I have not tested, and this explains why none of the Javascript examples refer to using <code>ActiveXObject</code> as a method to load XML files. (you will encounter <code>ActiveXObject</code> on other, older articles)</p>
<h2 id="localwebserver">Running XSLT in-browser via local webserver</h2>
<p>Serving a XML associated with a XSLT stylesheet in a local web service is a very easy way to apply an XSLT transformation if you just want to see the output in a browser. You'll need to ensure your XML file is associated with the XSLT file, by including this declaration in your XML file.</p>
<pre>
<code>
<? xml-stylesheet type="text/xsl" href="example.xsl" ?>
</code>
</pre>
<p>To apply the transformation you open the XML file in the browser and the transformation should be visible. The files should be in the part of filesystem that is accessible, in Linux systems this is usually <code>/var/www/html/</code>. If your XML and XSLT were stored in <code>/var/www/html/data/</code>, you would just enter <code>localhost/data/example.xml</code> and the transformation will be visible.</p>
<p>Note, on Linux systems, if the transformation is not applied you may have to check the file permissions. Using the terminal you can use <code>chgrp</code> and <code>chmod</code> however you need to provide the right permissions.</p>
<pre>
<code>
sudo chgrp [group] example.xml
sudo chmod 666 example.xml
</code>
</pre>
<p>On a public server you would want to lock down the files to read only, but whilst editing from the localhost, having the user with write access makes life a bit easier, for example if you navigate to the directory above the data folder, you could set the user for all files within the directory.</p>
<pre>
<code>
sudo chgrp [group] ./data -R
</code>
</pre>
<p>Be careful setting permissions for directories - as user you need the execute flag.</p>
<p>Opening the XML file directly from the file system (i.e not using localhost in the browser) will not work, because loading the stylesheet from the file system is a cross-origin request, and this is disallowed by browsers by default for security purposes.</p>
<p>Note however it used to be possible to use a file from the local file system - so you may see references to this on older webpages. There are ways to get around this by adjusting browser settings but this is not recommended from a security perspective - you forget to turn these settings back on and open yourselves to a vulnerablity when using the browser.</p>
<h3>Apache web server</h3>
<p>On linux, if you haven't already got a web server, downloading Apache2 is as easy as:</p>
<pre>
<code>
sudo apt-get install apache2
</code>
</pre>
<p>In your web-broswer, if you view source of the transformation you'll see the XML file without the XSLT transformation applied. The browser won't show the transformation in view-source, but if you open up the browsers interactive development tools you will see the transformation and be able to navigate through this. Alternatively, in Firefox you can select the text output in the browser, right click and select "View Selection Source".</p>
<p>What you might find with using this method to apply transformations is that the XSLT file may be cached and if you make changes to the XSLT file they are not applied. In Firefox developer tools there is a option within the browser to disable cache which is useful to see. Firefox developer tools will also show you which files are cached as not all files are, you can apply the option selectively.</p>
<h3>PHP web server</h3>
<p>An alternative for easy use, but not for public facing websites, is to use the built in PHP web server. If you already have PHP installed, this can be additionally downloaded using the PHP command line interface.</p>
<pre>
<code>
sudo apt install php8.1-cli
</code>
</pre>
<p>Once installed, using this webserver is as simple as setting the below in the terminal:</p>
<pre>
<code>
php -S localhost:8000
</code>
</pre>
<p>Entering <code>localhost:8000/</code> in your web browser should then serve the files within <code>/var/www/html</code></p>
<p>To load a specific folder on web server startup, append the terminal instruction like so:</p>
<pre>
<code>
php -S localhost:8000 -t "1. simple lookup"/
</code>
</pre>
<h2 id="javascript">Running XSLT using Javascript - XSLTProcessor()</h2>
<p>You can use the XSLT Processor API to apply XSLT transformations in the browser.</p>
<h3 id="javascript-xsltprocessor">XSLT Processor API</h3>
<p>On <a href="http://www.caniuse.com" title="Caniuse.com">caniuse.com</a> this feature is listed as supported on a variety of mainstream browsers however the website also notes "this feature is non-standard and should not be used without careful consideration. If you made a website that generated XSLT on the fly you might find some users not able to see the transformation.</p>
<p>Using XSLTProcessor to apply a transformation requires loading a file of document type <code>XMLDocument</code> - you can do this using AJAX <code>XMLHttpRequest()</code> or <code>fetch()</code> or a javascript library such as jQuery.</p>
<p>You will also require the webpage to be served using a local web server such as Apache.</p>
<p>It's sensible to test first if the browser has <code>XSLTProcessor()</code> available. To do this we can test the <code>window</code> object for the string <code>'XSLTProcessor'</code> and then provide an update in the browser console if available.</p>
<pre>
<code>
(function(){
if (!('XSLTProcessor' in window)) {
console.log('XSLTProcessor does not appear to be available in this browser. Please try another.');
return;
}else{
console.log('XSLTProcessor available.');
}
});
</code>
</pre>
<p>Here is an example of loading an XSLT and XML file into two <code>XMLDocuments</code> using <code>XMLHttpRequest()</code>, importing the XSLT stylesheet, then transforming the XML document using <code>XSLTProcessor()</code> and appending the resulting tranformation to a <code><div id="example"></div></code></p>
<p>The two key methods here are <code>xsltProcessor.importStylesheet(xslDoc);</code>, which imports the XSLT stylesheet into the XSLTProcessor the <code>xsltProcessor.transformToFragment(xmlDoc, document);</code> which applies the XSLT transformation.</p>
<p>To find out more about XSLTProcessor and its methods, <a href="https://developer.mozilla.org/en-US/docs/Web/API/XSLTProcessor" title="XSLTProcessor - Web APIs | MDN">Mozilla Developer Networks</a> has a good overview.</p>
<h4>Example 1 - XMLHttpRequest() and XSLTProcessor()</h4>
<pre>
<code>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>XSLT</title>
</head>
<body>
<div id="example"></div>
<script type="text/javascript">
(function(){
if (!('XSLTProcessor' in window)) {
console.log('XSLTProcessor does not appear to be available in this browser. Please try another.');
return;
}else{
console.log('XSLTProcessor available.');
}
const xsltProcessor = new XSLTProcessor();
const xslRequest = new XMLHttpRequest();
//true as third parameter indicates asynchronous request
xslRequest.open("GET", "data/example.xsl", true);
xslRequest.addEventListener("load", function() {
if(xslRequest.readyState == 4){
if (xslRequest.status == 200){
const xslDoc = xslRequest.responseXML;
xsltProcessor.importStylesheet(xslDoc);
}
}
});
xslRequest.send(null);
// load the XML file
const xmlRequest = new XMLHttpRequest();
//true as third parameter indicates asynchronous request
xmlRequest.open("GET", "data/example.xml", true);
xmlRequest.addEventListener("load", function() {
if(xmlRequest.readyState == 4){
if (xmlRequest.status == 200){
const xmlDoc = xmlRequest.responseXML;
const fragment = xsltProcessor.transformToFragment(xmlDoc, document);
document.getElementById("example").appendChild(fragment);
}
}
});
xmlRequest.send(null);
})();
</script>
</body>
</html>
</code>
</pre>
<p>The transformation is applied but using the existing example.xsl document we end up with two HTML documents within each other! That's not quite what we want. I'll call this version example-fragment.xsl. To keep things simple I will just put the text into HTML paragraphs.</p>
<pre>
<code>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
<xsl:template match="note">
<i>
<xsl:apply-templates/>
</i>
</xsl:template>
</xsl:stylesheet>
</code>
</pre>
<h3 id="javascript-jquery">jQuery $.ajax</h3>
<p>You can load XSLT and XML files using the <a href="https://jquery.com/" title="jQuery">jQuery</a> <code>$.ajax</code> functionality and then use <code>XSLTProcessor()</code> to apply the transformation.</a></p>
<p>Again, as <code>$.ajax</code> requires Ajax (i.e <code>XMLHttpRequest()</code>) a local webserver is required and for you to have jQuery loaded. I have used jQuery jquery-3.6.0 here, check what version you have available - bear in mind that there are differences between versions of jQuery.</p>
<h4>Example 2 - jQuery ($.ajax) and XSLTProcessor</h4>
<pre>
<code>
<!DOCTYPE html>
<html lang="en">
<head>
<meta encoding="utf-8">
<title>XSLT</title>
<script src="jquery-3.6.0.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/javascript">
$(function(){
if (!('XSLTProcessor' in window)) {
console.log('XSLTProcessor does not appear to be available in this browser. Please try another.');
return;
}else{
console.log('XSLTProcessor available.');
}
const xsltProcessor = new XSLTProcessor();
//XSLT file
$.ajax({
type: "GET",
url: "data/example.xsl",
dataType: "xml",
success: function (xsl) {
xsltProcessor.importStylesheet(xsl);
}
});
//XML file
$.ajax({
type: "GET",
url: "data/example.xml",
dataType: "xml",
success: function (xml) {
const fragment = xsltProcessor.transformToFragment(xml, document);
$("#example").append(fragment);
}
});
});
</script>
</body>
</html>
</code>
</pre>
<p>The output seen in the browser will be identical to the first example.</p>
<h4>Example 3 - jQuery ($.ajax Promises) and XSLTProcessor</h4>
<p>Here's another version of using <code>$.ajax</code> but with javascript Promises.</p>
<pre>
<code>
<!DOCTYPE html>
<html lang="en">
<head>
<meta encoding="utf-8">
<title>XSLT</title>
<script src="jquery-3.6.0.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/javascript">
$(function() {
if (!('XSLTProcessor' in window)) {
console.log('XSLTProcessor does not appear to be available in this browser. Please try another.');
return;
}else{
console.log('XSLTProcessor available.');
}
const xsltProcessor = new XSLTProcessor();
//XSLT file
$.ajax({
type: "GET",
url: "data/example.xsl",
dataType: "xml"
}).done(function(data){
xsltProcessor.importStylesheet(data);
}).fail(function(error){
console.log("Error: " + error.status + " " + error.statusText);
}).always(function(){
console.log("complete");
});
//XML file
$.ajax({
type: "GET",
url: "data/example.xml",
dataType: "xml"
}).done(function(data){
const fragment = xsltProcessor.transformToFragment(data, document);
$("#example").append(fragment);
}).fail(function(error){
console.log("Error: " + error.status + " " + error.statusText);
}).always(function(){
console.log("complete");
});
});
</script>
</body>
</html>
</code>
</pre>
<h4>Example 4 - jQuery ($.get) and XSLTProcessor</h4>
<p>We can use shorthand jQuery forms of <code>$.ajax</code> - <code>.load()</code> or <code>$.get()</code>. Here is an example using <code>.get()</code>, it is significantly shorter than the other methods.</p>
<pre>
<code>
<!DOCTYPE html>
<html lang="en">
<head>
<meta encoding="utf-8">
<title>XSLT</title>
<script src="jquery-3.6.0.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/javascript">
$(function(){
if (!('XSLTProcessor' in window)) {
console.log('XSLTProcessor does not appear to be available in this browser. Please try another.');
return;
}else{
console.log('XSLTProcessor available.');
}
const xsltProcessor = new XSLTProcessor();
//XSLT file
$.get("data/example.xsl", function(xsl) {
xsltProcessor.importStylesheet(xsl);
});
//XML file
$.get("data/example.xml", function(xml) {
const fragment = xsltProcessor.transformToFragment(xml, document);
$("#example").append(fragment);
});
});
</script>
</body>
</html>
</code>
</pre>
<h3>Using the fetch() API</h3>
<p>The fetch() API is seen as more modern replacement for <code>XMLHttpRequest()</code> and will work on pretty much everywhere except Internet Explorer - check on <a href="https://caniuse.com/?search=fetch" title="caniuse.com">caniuse.com</a> to verify which.
As the fetch() API is built into the browser its already available, where supported. We will still however need to serve our files from the server.</p>
<p>Here is an example using <code>fetch()</code> to load an XSLT and XML file to then using <code>XSLTProcessor()</code> to execute the stylesheet. The resulting transformation is appended into a <code><div id="example"></div></code></p>
<p>In this example we need to use <code>DOMParser</code> to convert the response from fetch into XML fragment.</p>
<p>To find out more about the fetch() API and its methods, <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API" title="Fetch - Web APIs | MDN">Mozilla Developer Networks</a> has a good overview.</p>
<h4>Example 5 - fetch() API and XSLTProcessor</h4>
<pre>
<code>
<!DOCTYPE html>
<html lang="en">
<head>
<meta encoding="utf-8">
<title>XSLT</title>
</head>
<body>
<div id="example"></div>
<script type="text/javascript">
(function(){
function fetchLoad(){
if(!('fetch' in window)) {
console.log('Fetch does not appear to be available in this browser. Please try another.');
return;
}
if(!('XSLTProcessor' in window)) {
console.log('XSLTProcessor does not appear to be available in this browser. Please try another.');
return;
}
if(!('DOMParser' in window)){
console.log('DOMParser does not appear to be available in this browser. Please try another.');
return;
}
const xsltProcessor = new XSLTProcessor();
const parser = new DOMParser();
//XSLT stylesheet
fetch('data/example.xsl').then(function(response){
// Do stuff with the response
if(response){
return response.text();
}
}).then(function(data) {
const xsl = parser.parseFromString(data, "application/xml");
xsltProcessor.importStylesheet(xsl);
})
.catch(function(error) {
console.log('Looks like there was a problem: ', error);
});
//XML file
fetch('data/example.xml').then(function(response){
// Do stuff with the response
if(response){
return response.text();
}
}).then(function(data) {
const xml = parser.parseFromString(data, "application/xml");
const fragment = xsltProcessor.transformToFragment(xml, document);
document.getElementById("example").appendChild(fragment);
})
.catch(function(error) {
console.log('Looks like there was a problem: ', error);
});
}
window.addEventListener("load", fetchLoad, false);
})();
</script>
</body>
</html>
</code>
</pre>
<h3>Can we use fetch() API with <code>await and async</code>?</h3>
<p>The keywords <code>await and async</code> are a relatively new feature to javascript and can allow functions to behave asynchronously. The general usage is as follows:</p>
<pre>
<code>
async myfunction function(){
//call a function that is likely to take time to complete fully
let data = await (function)
}
//myfunction behaves asynchronously
myfunction();
</code>
</pre>
<p>The keyword <code>async</code>, when used before a function, returns a javascript Promise(). It does this even if the return within the function is not specified as a Promise().</p>
<h4>Example 6 - fetch() API with await and async</h4>
<pre>
<code>
<!DOCTYPE html>
<html lang="en">
<head>
<meta encoding="utf-8">
<title>XSLT</title>
</head>
<body>
<div id="example"></div>
<script type="text/javascript">
(function(){
function fetchLoad(){
if(!('fetch' in window)) {
console.log('Fetch does not appear to be available in this browser. Please try another.');
return;
}
if(!('XSLTProcessor' in window)) {
console.log('XSLTProcessor does not appear to be available in this browser. Please try another.');
return;
}
if(!('DOMParser' in window)){
console.log('DOMParser does not appear to be available in this browser. Please try another.');
return;
}
const xsltProcessor = new XSLTProcessor();
const parser = new DOMParser();
loadFile("data/example.xsl").then(data => {
//this needs to be let
const xsl = parser.parseFromString(data, "application/xml");
xsltProcessor.importStylesheet(xsl);
});
loadFile("data/example.xml").then(data => {
const xml = parser.parseFromString(data, "application/xml");
const fragment = xsltProcessor.transformToFragment(xml, document);
document.getElementById("example").appendChild(fragment);
});
}
async function loadFile(filepath){
const response = await fetch(filepath);
if(!response.ok){
console.log('Looks like there was a problem: ', response.status);
}
const text = await response.text();
return text;
}
window.addEventListener("load", fetchLoad, false);
})();
</script>
</body>
</html>
</code>
</pre>
<p>In our examples we need the XSLT file loaded before the XML file. Javascript Promises() provide a <code>.then()</code> method we can use to call javascript in a sequential order we need.</p>
<h4>Example 7 - fetch() API with await and async, using .then()</h4>
<pre>
<code>
<!DOCTYPE html>
<html lang="en">
<head>
<meta encoding="utf-8">
<title>XSLT</title>
</head>
<body>
<div id="example"></div>
<script type="text/javascript">
(function(){
function fetchLoad(){
if(!('fetch' in window)) {
console.log('Fetch does not appear to be available in this browser. Please try another.');
return;
}
if(!('XSLTProcessor' in window)) {
console.log('XSLTProcessor does not appear to be available in this browser. Please try another.');
return;
}
if(!('DOMParser' in window)){
console.log('DOMParser does not appear to be available in this browser. Please try another.');
return;
}
const xsltProcessor = new XSLTProcessor();
const parser = new DOMParser();
loadFile("data/example.xsl").then(data => {
//this needs to be let
const xsl = parser.parseFromString(data, "application/xml");
xsltProcessor.importStylesheet(xsl);
}).then(loadFile("data/example.xml").then(data => {
const xml = parser.parseFromString(data, "application/xml");
const fragment = xsltProcessor.transformToFragment(xml, document);
document.getElementById("example").appendChild(fragment);
}));
}
async function loadFile(filepath){
const response = await fetch(filepath);
if(!response.ok){
console.log('Looks like there was a problem: ', response.status);
}
const text = await response.text();
return text;
}
window.addEventListener("load", fetchLoad, false);
})();
</script>
</body>
</html>
</code>
</pre>
<h3>XMLDocument.load()</h3>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLDocument/load" title="XMLDocument - Web APIs | MDN"><code>XMLDocument.load()</code></a></p>
<p>It's worth noting that this used to work, but now it doesn't (at least not in Firefox).</p>
<h3>What about XML Data Islands?</h3>
<p>There used to be a notion XML "Data Islands" in HTML. These are
pieces of XML incorporated within the HTML of the webpage. This was at a time before widespread use of databases in website development. Data Islands were supported within IE via an unofficial <code><XML></code> element tag and used in a Microsoft MSXML focus (i.e aimed for use in Internet Explorer).</p>
<p>See this old Microsoft article from 2016 which explains the approach <a href="https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms766512(v=vs.85)" title="Microsoft Data Islands">XML Data Islands<a></p>
<h3>Include XML and XSLT within HTML</h3>
<p>It is possible to include SVG (a XML vocabularly to define 2D graphics) inline into HTML. XSLT is just another XML vocabularly so why you incorporate XSLT into the HTML page without having an external file to load. Surely we could do the same for the XML we want to transform also?</p>
<p>i.e SVG can be inline within HTML like so.</p>
<pre>
<code>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Inline SVG</title>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg">
<text x="10" y="50" font-size="30">Inline SVG</text>
</svg>
</body>
</html>
</code>
</pre>
<p>Including XML within HTML is <a href="https://www.w3.org/2010/html-xml/snapshot/report.html#uc04"
title="W3C - HTML/XML Task Force Report">tricky!</a></p>
<p>The following examples are not recommended - nor are they expected to work!</p>
<h4>Example 8 - XML and XSLT within HTML (not a recommended approach!)</h4>
<p>A key advantage of having a separate file with the XML is when you want to update data you just have to edit the XML file.
What is the point of having the data scource embedded within a file you won't likely change.</p>
<p>Here is an example of incorporting the XML and XLST within HTML document and applying a transformation. Both the full context of the example XML and XSLT documents are simply copied into two separate <code><div></code> which are then hidden from view using the CSS <code>display:none</code> and with an ID so each can be referenced in the Javascript.</p>
<p>The XML is easily treatable as a XML fragment, the XSLT is a bit more tricky. The solution below - there may be other approaches - serializes XML as a string using <code>XMLSerializer</code>, then is converted back into XML using <code>DOMParser</code>.
<p>An this approach avoids loading any files using AJAX, a webserver is not required.</p>
<p>Tested in Firefox only.</p>
<pre>
<code>
<!DOCTYPE html>
<html lang="en">
<head>
<meta encoding="utf-8">
<title>XSLT</title>
</head>
<body>
<!--XML-->
<div id="xml" style="display:none">
<!-- Text provided by Perseus Digital Library, with funding from The Annenberg CPB/Project. Original version available for viewing and download at http://www.perseus.tufts.edu/hopper/. -->
<extract>
<milestone ... />
<milestone ... />
<milestone ... />
<milestone ... />
<milestone ... />
</extract>
</div>
<!--XSLT-->
<div id="xslt" style="display:none">
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
<xsl:template match="note">
<i>
<xsl:apply-templates/>
</i>
</xsl:template>
</xsl:stylesheet>
</div>
<div id="example"></div>
<script type="text/javascript">
(function(){
function documentLoaded(){
if(!('XSLTProcessor' in window && 'DOMParser' in window && 'XMLSerializer' in window)){
console.log('XSLTProcessor does not appear to be available in this browser. Please try another.');
return;
}
if(!('XMLSerializer' in window)) {
console.log('XMLSerializer does not appear to be available in this browser. Please try another.');
return;
}
let parser = new DOMParser();
let xsltDoc, xmlDoc = null;
//XSLT stylesheet
let xslt = document.getElementById("xslt");
if(xslt){
//convert to XSLT to string
const s = new XMLSerializer();
const d = xslt.firstElementChild;
let str = null;
try{
str = s.serializeToString(d);
console.log(str);
}catch(error){
//we can check here for instances of particular errors
console.log(error);
}
//now convert that string into XMLDocument
let parser = new DOMParser();
xsltDoc = parser.parseFromString(str, "application/xml");
const errorNode = xsltDoc.querySelector('parsererror');
if (errorNode) {
console.log(errorNode);
} else {
console.log("parsing successful");
}
}
//XML
console.log("XML document");
let xml = document.getElementById("xml");
if(xml){
xmlDoc = document.implementation.createDocument(null, "", null);
xmlDoc.appendChild(xml);
}
//XSLT Processors and import XSLT
let xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(xsltDoc);
//now we can run the transformation and save to fragment
let fragment = xsltProcessor.transformToFragment(xmlDoc, document);
//now show append the transformation to the <div id="example"> to present
document.getElementById("example").appendChild(fragment);
}
window.addEventListener("load", documentLoaded, false);
})();
</script>
</body>
</html>
</code>
</pre>
<h3>What about using <code><iframe></code> or <code><object></code> to load XML and XSLT?</h3>
<h4>Example 7 - using <iframe></h4>
<p>You can use an <code><iframe></code> to load a XML document within a webpage. What is presented by default in the browser varies - Firefox gives you a indented output of the XML document, others may just show the text content. If you call an <code><iframe></code> like so and the XML document is associated with the XSLT stylesheet (reminder: <code><?xml-stylesheet type="text/xsl" href="example.xsl"?></code>) you will see the XSLT transformation applied in the browser.</p>
<pre>
<code>
...
<iframe id="xslt-iframe" src="data/example.xml" width="800" height="300">
<p>iframe is not supported</p>
</iframe>
...
</code>
</pre>
<p>Note that <code><frame></code> and <code><frameset></code> are obsolete in HTML5, so any idea of loading a frame with the XML document and another with the XSLT document is not going to be valid.</p>
<p><code><iframe></code> does not recognise a document with file extension .xsl so if you wanted to show the XSLT within the webpage as opposed to just applying the transformation you will need to change the file extension to .xml. You may also need to remove the <code><?xml-stylesheet type="text/xsl" href="example.xsl"?></code> to get the browser to output the XSLT.</p>
<p>Note that <code><iframes></code> can lead to vulnerabilities if you allow the user to interact with the loaded document - this is a complicated topic, so use carefully.</p>
<p>In this example the XSLT is loaded via the <code>iframe</code> then is serialized into a string, which is then added to the <code><code id="output"><code></code></p></p>
<pre>
<code>
<!DOCTYPE html>
<html lang="en">
<head>
<meta encoding="utf-8">
<title>XSLT</title>
</head>
<body>
<iframe id="xslt-iframe" src="data/examplexsl.xml" width="800" height="300">
<p>iframe is not supported</p>
</iframe>
<pre>
<code id="output">
</code>
</pre>
<script type="text/javascript">
(function(){
function documentLoaded(){
if(!('XMLSerializer' in window)){
console.log('XSLTProcessor does not appear to be available in this browser. Please try another.');
return;
}
//get the iframe
let xslt = document.getElementById("xslt-iframe");
//get the output location
const output = document.getElementById("output");
//getting the document
let xsltDoc = xslt.contentDocument.documentElement;
//serialize the XML to a string
let s = new XMLSerializer();
let str = null;
try{
str = s.serializeToString(xsltDoc);
output.textContent = str;
}catch(error){
console.log(error);
}
}
window.addEventListener("load", documentLoaded, false);
}) ();
</script>
</body>
</html>
</code>