Select DIV tag with XSLT? - xslt

I'm trying to create a new XHTML document from another XHTML document. I only want to use some of the DIV tags in the old XHTML document, but I'm not sure if I'm doing it right. To start, if I want to select a special DIV tag with ID = mbContent, could I use
<xsl:template match="x:div[#id='mbContent']">
This DIV tag contains other DIVs and content like images and so on. How do I do if I want to use the same CSS style that is applied to the content? Is there a way to copy the CSS style or do I have to add new CSS style and how do I do that? Since the new XHTML document is going to be part of antother XHTML, I cant use HEAD tag and put a reference to the CSS stylesheet that way.
Hmm, but if I use the CSS stylesheet that is going to be in the HEAD of the main XHTML documnet, perhaps I could apply that CSS styles to this DIV, or? How do I apply styles in the new XHTML document?
I'm a little bit confused, but I hope my question isn't to confusing?! :)
Hi! I need some new help since the code below isn't working for me. It's this that isn't working
xmlns:x="http://www.w3.org/1999/xhtml" and "x:div[#id='mbContent']"
I think it's because I'm using a CMS tool that has a proxy module that not accept this code for some strange reason. Therefore I'm looking for some alternative solution to add CLASS or ID and also add values to DIV elements by using this instead xsl:apply-templates select="//*[#id='mbSubMenu']" and also use copy as in the example below? Preciate some new help! Thanks! :)

The Xpath used in the expression is fine until you are using 'x' as xmlns in the XSLT document.
The template will match for the <div> provided its id is mbContent and the selected context will have all the descendents.
You can change the inline CSS for the elements. Since you said that this part is going to be within some other XHTML document. You can choose XML as output.
Change the inline CSS if you want to.
You can also assign them different classes so that it takes global styles automatically.
The idea is that given a XML document you are transforming it to another XML document.
Therefore, you can apply styles as you like it.
I hope that answers your question.
P.S. use proper xmlns in the XPath expression.
Let's assume following is the HTML doc.
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title></title>
</head>
<body>
<div id="mbContent">
<div>
<span>Some complex structure</span>
</div>
</div>
</body>
Apply the following XSL
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:x="http://www.w3.org/1999/xhtml"
exclude-result-prefixes="x">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="x:div[#id='mbContent']">
<xsl:copy>
<xsl:attribute name="class">
<xsl:text>someNewStyle</xsl:text>
</xsl:attribute>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
This will result in the following output.
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title/>
</head>
<body>
<div class="someNewStyle" id="mbContent">
<div>
<span>Some complex structure</span>
</div>
</div>
</body>
You can change the XSL to suit your need.
Regards,
Ravish.

Related

Why doesn't my SVG display when I try to use the XSLT output method "html"?

Apologies if this is a simple question as I'm fairly novice at XSLT. I'm having an issue where the SVG image I'm generating in XSLT won't display if my output method is set to html. If I open the desired xml file, transform it, and view it in browser (IE v11), the whole document loads with exception to the SVG image itself. In IE, if I right click the document and select "view source" I can see the SVG information sitting there, right where its expected to be.
If I set the output method to xml and open in IE, the SVG images do appear but the document structure isn't there (but to reiterate: the goal is to output in html as it's used in other processes further down the line)
Below is a snippet of a small, simple testing XSLT and the post transformation xml seen from "view source" (leaving out unrelated stuff). I'm trying to get this working on a small scale before I tackle the larger picture.
Also to note, the svg:text tag does display the text mentioned, although it seems more like a simple text entry than part of an image.
For those familiar with it, the majority of functionality comes from RenderX's XSLT barcode generator.
XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns="http://www.w3.org/1999/xhtml" xmlns:svg="http://www.w3.org/2000/svg">
<xsl:import href="code128.xsl"/>
<xsl:output method='html' media-type="image/svg" encoding='UTF-8' indent='yes'/>
<xsl:template match="/">
<html lang="en">
<head>
<title>SVG bar code examples</title>
</head>
<body>
<h1>SVG bar code examples</h1>
<ul>
<xsl:apply-templates select="//barcode"/>
</ul>
</body>
</html>
</xsl:template>
<xsl:template match="barcode">
<li>
<xsl:call-template name="barcode-code128">
.
<!--Parameters here-->
.
</xsl:call-template>
</li>
</xsl:template>
</xsl:stylesheet>
Post Transformation XML:
<?xml version="1.0" encoding="utf-8"?>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:svg="http://www.w3.org/2000/svg">
<head>
<title>SVG bar code examples</title>
</head>
<body>
<h1>SVG bar code examples</h1>
<ul>
<li>
<svg:svg width="29.494444444444443mm" height="12.7mm" viewBox="0 0 10618 4572">
<svg:path d="M 686 686 l 0 2286 138 0 0 -2286 z m ..."(truncated due to length)>
<svg:text x="6068" y="4420" text-anchor="middle" font-family=...
</svg:svg>
</li>
.
<!--More SVG images here-->
.
</ul>
</body>
</html>
If you want to use the output method html then you should use HTML syntax and neither HTML 4 nor HTML5 or what is simply called HTML now supports namespaces, in particular not prefixed element names. So for your SVG elements to be recognized in text/html you need simply svg, path and text as the element names without any prefix, the only allowed "namespace" declaration would be xmlns="http://www.w3.org/2000/svg" on the svg element.
I am also not sure about IE's SVG support, perhaps it is only enabled in standards compliant rendering mode, meaning, for method="html" it is recommended to have <xsl:output method="html" indent="yes" version="5" doctype-system="about:legacy-doctype"/>, as that doctype-system is supposed to set older browsers distinguishing between quirks mode and standard compliant mode to the latter mode.
If your used library creates prefixed elements then you would need to run them through an additional transformation step to strip the namespace prefix which, in a single transformation, is in XSLT 1 only possible using a proprietary extension function to convert a result tree fragment into a node-set:
<xsl:template match="barcode">
<li>
<xsl:variable name="barcode-rtf">
<xsl:call-template name="barcode-code128">
.
<!--Parameters here-->
.
</xsl:call-template>
</xsl:variable>
<xsl:apply-templates select="msxml:node-set($barcode-rtf)/node()" xmlns:msxml="urn:schemas-microsoft-com:xslt" mode="strip-svg-prefix"/>
</li>
</xsl:template>
<xsl:template match="#* | node()" mode="strip-svg-prefix">
<xsl:copy>
<xsl:apply-templates select="#* | node()" mode="strip-svg-prefix"/>
</xsl:copy>
</xsl:template>
<xsl:template match="svg:*" mode="strip-svg-prefix" xmlns:svg="http://www.w3.org/2000/svg">
<xsl:element name="{local-name()}" namespace="http://www.w3.org/2000/svg">
<xsl:apply-templates select="#* | node()" mode="strip-svg-prefix"/>
</xsl:copy>
</xsl:template>
Note that, for text/html and HTML 4 or HTML5 as the transformation target with method="html", I would recommend to use no namespace at all for the HTML elements, i.e. to remove the XHTML namespace declaration you have. Otherwise serialization of empty elements might not give the proper HTML syntax.

Clientside XSLT in Mozilla/Firefox fails

I have a result document that renders in Chrome, but not Mozilla/Firefox.
I believe it is because there is top level leading whitespace (two blank lines before the <!DOCTYPE html).
How can I change this transform to not have leading whitespace (fiddle)?
XML:
<?xml-stylesheet href="/css/my.xsl" type="text/xsl"?>
<webpage>
<title>Book</title>
<auth>Mike</auth>
<container-content>
<p>foo1</p>
<p>foo2</p>
</container-content>
</webpage>
XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="1.0">
<xsl:output
method="xml"
indent="yes"
encoding="UTF-8"
omit-xml-declaration="yes"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"/>
<xsl:strip-space elements="*"/>
<xsl:template match="text()"/>
<xsl:template match="/">
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=utf-8"/>
</head>
<body>
<xsl:copy-of select="//container-content/*"/>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Result:
- a blank line here -
- and here -
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>
<body>
<p>foo1</p>
<p>foo2</p>
</body>
</html>
Alternatively, I may be incorrect, and the two blank lines are not the cause of the failed render in Mozilla/Firefox. I have a hard time troubleshooting client side transforms.
Side note: I've developed in Saxon 6.5, thinking Saxon best approximates what browsers do. I could be wrong. I note Xalan does not put in leading whitespace.
I just ran your stylesheet with Saxon 6.5 and indeed, it outputs two blank lines, which are removed if you change the xsl:output to be without indentation and with xml declaration. However, I believe this to be a bug in Saxon 6.5 (a small one, as the whitespace is not significant).
Running it with other XSLT 1.0 processors show no whitepace. However, as said in my comment, the whitespace is insignificant, as browsers do not serialize anyway. (note: apparently, browsers do some kind of serialization, in the sense that they look to whether you use XML or HTML output).
I ran your example with Firefox and it "just works". Since your stylesheet does a simple copy of the XML, it shows just the text. If I change the xsl:output to HTML and add a few lines to be sure I am running it correctly (I added an <h1>Hello</h1>, it shows the HTML.
I'm not sure what you expect the browser to show, but my guess is not XML, but (X)HTML. XSLT 1.0 is not very good with XHTML (it is supported in XSLT 2.0, but that is not supported by browsers), but works fine with HTML.
I modified your stylesheet as follows:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:template match="text()"/>
<xsl:template match="/">
<html>
<head />
<body>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="title">
<h1><xsl:value-of select="."/></h1>
</xsl:template>
<xsl:template match="auth">
<p>Author: <xsl:value-of select="." /></p>
</xsl:template>
</xsl:stylesheet>
And in Firefox and Chrome it renders as follows:
Note (1): if you do not run it from a web server (either local or remote), it will not run in either Firefox or Chrome because of security restrictions.
Note (2): to view the rendered XML or HTML, use the Inspect Element feature of the developer tools of either Chrome or Firefox.
Note (3): you do not need to use the meta-tag, as the specification requires this meta tag to be output as soon as it recognizes that HTML is output.
Note (4) if you are unsure whether or not Firefox is loading your stylesheet correctly, have a look using Firebug, it should show something like this (mark the "200 OK"):
If you want to transform to XHTML then you need to make sure you use the XHTML namespace for your result elements so put
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/1999/xhtml"
version="1.0">
on your stylesheet, as otherwise with output method xml your elements in no namespace are not recognized as XHTML elements by Mozilla.
As your input p elements are also in no namespace you can not copy them through but have to write a template for them
<xsl:template match="*">
<xsl:element name="{local-name()}"><!-- assumes you have the namespace declaration suggested above -->
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
and then use <xsl:apply-templates select="//container-content/*"/> instead of the copy-of. And in that case the <xsl:template match="text()"/> needs to be removed as otherwise the text of the transformed p elements would not show up.

saxon including boolean itemscope value and closing source tag in html output

I'm using Saxon-HE 9.6.0.1J from Saxonica to generate HTML documents (xsl:output method="html"). It's generally good at omitting the value of boolean attributes and closing tags for empty elements, but I've found a few situations where it fails:
The microdata itemscope="itemscope" attribute is not output as simply itemscope
empty source elements are given closing tags
Here is an example stylesheet that demonstrates the problem:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html" encoding="utf-8" include-content-type="no"/>
<xsl:template match="/">
<html>
<head>
<meta charset="UTF-8" />
<title>HTML test</title>
</head>
<body>
<div itemscope="itemscope" itemtype="http://example.com/dummy/">
<span itemprop="prop1">val1</span>
</div>
<audio autoplay="autoplay" controls="controls">
<source type="audio/mpeg" src="example.mp3" />
<source type="audio/x-wav" src="example.wav" />
</audio>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Sample XML:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="example.xsl"?>
<example/>
Command:
java -cp saxon9he.jar net.sf.saxon.Transform -s:example.xml -a
Results:
<html>
<head>
<meta charset="UTF-8">
<title>HTML test</title>
</head>
<body>
<div itemscope="itemscope" itemtype="http://example.com/dummy/"><span itemprop="prop1">val1</span></div>
<audio autoplay controls>
<source type="audio/mpeg" src="example.mp3"></source>
<source type="audio/x-wav" src="example.wav"></source>
</audio>
</body>
</html>
As demonstrated, meta is properly empty but source is not, and the values for autoplay and controls are properly omitted but not for itemscope.
Is this a bug, or am I missing the solution to tell Saxon how to treat those elements and attributes? I've searched the docs on saxonica.com and questions here for a clue, but not found anything.
Thanks in advance!
Quick update: In XSLT 3.0, you can specify the #html-version attribute, which you can set to 5.0 if you want to use XHTML5. Doing so solved the issue of at <source> for me while still using #method="xhtml".
The "source" element is recognized as an empty element if you specify version="5.0" on xsl:output.
The list of attributes that Saxon recognizes as boolean attributes when you specify method="html" version="5.0" comes from here:
http://www.w3.org/TR/html5/index.html#attributes-1
which does not include "itemscope". I'm afraid I can't help you with the history of how it comes to be present in some flavours of HTML and not in the W3C flavour, but Saxon inevitably follows the W3C specs.
Perhaps we should provide some way of extending the list (if you're really keen you can do it by writing your own serializer factory class that customizes the Saxon serializer, but that's serious hackery).

XSLT: select elements that have multiple #class values based off of 1 of those values

I have an XML document which uses multiple #class values, like below:
<html>
<head>
<title>This is the title.</title>
</head>
<body>
<div class="sect1 rights">
<p>This is the first paragraph.</p>
</div>
</body>
</html>
However, my XSLT below does not work when both #class values are present. It will only work if <div class="rights">.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[#class='rights']">
<p>Rights have not cleared to show this material.</p>
</xsl:template>
I don't think this is a priority of templates problem (from the identity template) as I'm not getting a warning of that. Is there a way to select this content? I won't always know what other class values are assigned besides "rights" so I can't use *[#class='sect1 rights'] as sect1 will change throughout the document.
Since you're using XSLT 2.0, you can use tokenize(). This a much simpler form of the concat() solution that #user3016153 gave (both forms are much more accurate than the simple contains()).
<xsl:template match="*[tokenize(#class,'\s')='rights']">
<p>Rights have not cleared to show this material.</p>
</xsl:template>
It is also very easy to extend if you want to match more than one possible class:
<xsl:template match="*[tokenize(#class,'\s')=('rights','otherclass')]">
<p>Rights have not cleared to show this material.</p>
</xsl:template>
In your example, the "class" attribute has only a single value: the string "sect1 rights". If you want to test for the inclusion of the string "rights" in that value, use:
<xsl:template match="*[contains(#class,'rights')]">
or perhaps:
contains(concat(' ', #class, ' '), ' rights ')
if you want to ensure the presence of "rights" as a word (i.e. not as part of "copyrights", for example).

How to include all the contents of a file in the <body> using XSLT?

I want to have a separate file that holds some javascript functions. When XSLT processes my application, i want it to output all the contents of this file to the HTML. I do not want to reference a library, but instead have all the functions inside inside my html.
I know of <xsl:include> but i cannot include anything inside or <body> tags.
Is this possible?
Well assuming your file (e.g. scripts.xml) to be included is XML e.g. has a contents like
<script type="text/javascript">
function foo() { ... }
function bar() { ... }
...
</script>
then in XSLT you can simply use
<xsl:template match="/">
<html>
<body>
<xsl:copy-of select="document('scripts.xml')/script"/>
</body>
</html>
</xsl:template>
If that does not help then you need to explain in more details what kind of file you have, which XSLT version you use (XSLT 2.0 can also read in non-XML plain text files like Javascript code).
[edit]
Here is an XSLT 2.0 sample using unparsed-text (requires an XSLT 2.0 processor like Saxon or AltovaXML):
<xsl:template match="/">
<html>
<body>
<script type="text/javascript">
<xsl:value-of select="unparsed-text('file.js')"/>
</script>
</body>
</html>
</xsl:template>
Use the unparsed-text() function to read the content of a text file.
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<html>
<body>
<xsl:sequence select="unparsed-text('YourFile.js')"/>
</body>
</html>
</xsl:template>
</xsl:stylesheet>