LizHelpMe
asked on
XSLT, Javascript & Breadcrumb problem
I have the above code working really well and consistently being able to successfully
click through my xml and step back again. It's like Windows Explorer through a web browser.
I have managed to get this to work across all browsers. Previously I was unable to do this
cross browser because I used import and also passed paramaters into the style sheet
from javascript with Sarissa. I have found that passing parameters is not possible with webkit
browsers, so I have found this way. I just need some help on a decent approach to creating a
breadcrumb trail.
Can anyone give decent assistance on how to create a breadcrumb trail with the above code.
click through my xml and step back again. It's like Windows Explorer through a web browser.
I have managed to get this to work across all browsers. Previously I was unable to do this
cross browser because I used import and also passed paramaters into the style sheet
from javascript with Sarissa. I have found that passing parameters is not possible with webkit
browsers, so I have found this way. I just need some help on a decent approach to creating a
breadcrumb trail.
Can anyone give decent assistance on how to create a breadcrumb trail with the above code.
Here is the XML:
<?xml version="1.0" encoding="utf-8"?>
<items id="0">
<region id="1" name="New Items">
<group id="8" name="Apple">
<region id="9" name="artichoke">
<region id="15" name="aeroplane">
<extension id="10008" name="0005" />
<extension id="10001" name="0009" />
</region>
</region>
</group>
</region>
</items>
Here is the XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<div id="testDiv">
<xsl:if test="items/@parentId!='0'">
<div id="myBackDiv" onmouseover="reportOn(this.id)" onmouseout="reportOff(this.id)">
<xsl:attribute name="onclick">
<xsl:text>StepBack('</xsl:text>
<xsl:value-of select="items/@parentId"/>
<xsl:text>');</xsl:text>
</xsl:attribute>
<img src="Images/png/back.png" width="32px" height="32px"/>
<span>
<xsl:attribute name="style">
<xsl:text> font-style:bold; text-decoration:underline;</xsl:text>
</xsl:attribute>
<xsl:text>up again</xsl:text>
</span>
</div>
</xsl:if>
<xsl:for-each select="items/*">
<xsl:if test ="self::region | self::division | self::group | self::site" | self:extension>
<div id="{@id}" onmouseover="modalOn(this.id)" onmouseout="modalOff(this.id)">
<xsl:if test="*[@id]">
<xsl:attribute name="onclick">
<xsl:text>StepThrough('</xsl:text>
<xsl:value-of select="@id"/>
<xsl:text>');</xsl:text>
</xsl:attribute>
</xsl:if>
<span>
<xsl:attribute name="style">
<xsl:if test="*[@id]">
<xsl:text> font-style:bold; text-decoration:underline; cursor:pointer;</xsl:text>
</xsl:if>
</xsl:attribute>
<xsl:value-of select="@name" />
</span>
</div>
</xsl:if>
</xsl:for-each>
</div>
</xsl:template>
</xsl:stylesheet>
Here is the Javscript:
var xml;
if (window.XMLHttpRequest) {
xhttp = new XMLHttpRequest();
}
else {
xhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
function loadXMLDoc(dname) {
xhttp.open("GET", dname, false);
xhttp.send("");
return xhttp.responseXML;
}
function displayResult(pXml) {
xsl = loadXMLDoc("cdcatalog.xsl");
// code for IE
if (window.ActiveXObject) {
ex = pXml.transformNode(xsl);
document.getElementById("availableSearchItems").innerHTML = ex;
}
// code for Mozilla, Firefox, Opera, Chrome, etc.
else if (document.implementation && document.implementation.createDocument) {
xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(xsl);
resultDocument = xsltProcessor.transformToFragment(pXml, document);
document.getElementById('availableSearchItems').innerHTML = "";
document.getElementById("availableSearchItems").appendChild(resultDocument);
}
}
function StepThrough(pId) {
var newTest = "<?xml version='1.0' encoding='utf-8'?><items id='0' parentId='" + pId + "'>";
var nodeFrom = xml.selectSingleNode('//*[@id=' + pId + ']');
var name = nodeFrom.getAttribute("name");
for (var i = 0; i < nodeFrom.childNodes.length; i++) {
var xmlString = new XMLSerializer().serializeToString(nodeFrom.childNodes(i));
newTest += xmlString;
}
newTest += "</items>";
var xmlObj = (new DOMParser()).parseFromString(newTest, "text/xml");
displayResult(xmlObj);
}
function StepBack(pId)
{
var nodeFrom = xml.selectSingleNode('//*[@id=' + pId + ']');
var parentItem = nodeFrom.parentNode;
var parentId = parentItem.getAttribute("id");
StepThrough(parentId)
}
ASKER
Hi, As far as I am aware there is no way to pass a param to xslt when using webkit browsers, though I could easily be wrong. I just haven't found any documentation on that. Basically, you got what I'm trying to say down to a tee. My way around it is by passing the new xml into xslt each time. Thus losing any paramter.
This bit where I step through and re-create the xml with all childnodes
function StepThrough(pId) {
var newTest = "<?xml version='1.0' encoding='utf-8'?><items id='0' parentId='" + pId + "'>";
var nodeFrom = xml.selectSingleNode('//*[ @id=' + pId + ']');
var name = nodeFrom.getAttribute("nam e");
for (var i = 0; i < nodeFrom.childNodes.length ; i++) {
var xmlString = new XMLSerializer().serializeT oString(no deFrom.chi ldNodes(i) );
newTest += xmlString;
}
newTest += "</items>";
var xmlObj = (new DOMParser()).parseFromStri ng(newTest , "text/xml");
displayResult(xmlObj);
}
On the above code I do believe I make it well-formed. It does work. I figured a way to do this would be to add an extra attribute to the top level items tag like I do for parentId which is used for the back button.
var newTest = "<?xml version='1.0' encoding='utf-8'?><items id='0' parentId='" + pId + "'>";
Then I can access that Id and get reversing through my xml looking for parentNodes all the way until there are none. Can you help mme with this. Doing it this way will mean creating a completely new breadcrumb for every time I step through/backward in the xml.
This bit where I step through and re-create the xml with all childnodes
function StepThrough(pId) {
var newTest = "<?xml version='1.0' encoding='utf-8'?><items id='0' parentId='" + pId + "'>";
var nodeFrom = xml.selectSingleNode('//*[
var name = nodeFrom.getAttribute("nam
for (var i = 0; i < nodeFrom.childNodes.length
var xmlString = new XMLSerializer().serializeT
newTest += xmlString;
}
newTest += "</items>";
var xmlObj = (new DOMParser()).parseFromStri
displayResult(xmlObj);
}
On the above code I do believe I make it well-formed. It does work. I figured a way to do this would be to add an extra attribute to the top level items tag like I do for parentId which is used for the back button.
var newTest = "<?xml version='1.0' encoding='utf-8'?><items id='0' parentId='" + pId + "'>";
Then I can access that Id and get reversing through my xml looking for parentNodes all the way until there are none. Can you help mme with this. Doing it this way will mean creating a completely new breadcrumb for every time I step through/backward in the xml.
ASKER
Hi, You said about using the DOM to change a param element actually in the XSLT. I didnt know this could be done at all. This sounds an interesting way. I'm getting on much better with XSLT these days. It's good stuff.
Well, for myself, I would definitely prefer changing the parameter using the DOM way and get rid of all the navigation code in JavaScript.
ASKER
Could you show me how to do it? Thanks.
have to get something out for a customer this afternoon, will work on this later today
ASKER
Ok so I have the breadcrumb object. It is just passing an id field into my xslt file and then invoking the below template to successfully reverse through each step until the end and display the breadcrumb. I understand that you must be very busy and I can wait however long you like.
Thanks again.
Thanks again.
<xsl:template name="breadcrumb">
<xsl:param name="node"/>
<xsl:choose>
<xsl:when test="$node/parent::region">
<xsl:call-template name="breadcrumb">
<xsl:with-param name="node" select="$node/parent::region[1]"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$node/parent::division">
<xsl:call-template name="breadcrumb">
<xsl:with-param name="node" select="$node/parent::division[1]"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$node/parent::site">
<xsl:call-template name="breadcrumb">
<xsl:with-param name="node" select="$node/parent::site[1]"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$node/parent::group">
<xsl:call-template name="breadcrumb">
<xsl:with-param name="node" select="$node/parent::group[1]"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$node/parent::extension">
<xsl:call-template name="breadcrumb">
<xsl:with-param name="node" select="$node/parent::extension[1]"/>
</xsl:call-template>
</xsl:when>
</xsl:choose>
<xsl:text> \\ </xsl:text>
<xsl:text> \\ </xsl:text>
<span id="{$node/@id}" onmouseover="BreadCrumbOn(this.id)" onmouseout="BreadCrumbOff(this.id)" onclick="displayDirectory('{$node/@id}', '{ $node/@name }')">
<xsl:value-of select="$node/@name"/>
</span>
</xsl:template>
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
I gave your way a good go. I just keep getting wrong number of arguments, invalid property assignment. gertone mentioned I could use selectSingleNode. Im sure he will give me the help I need as he is a complete genius. Thanks for giving me tip though Im going to continue hacking away at it until I get some where.
Here is an interesting way:
http://stackoverflow.com/questions/733345/how-to-pass-a-parameter-from-an-url-to-a-xsl-stylesheet/746687#746687
http://stackoverflow.com/questions/733345/how-to-pass-a-parameter-from-an-url-to-a-xsl-stylesheet/746687#746687
Okeee:
The XSLTProcessor in Firefox also allows you to set XSLT parameters. The setParameter() method accepts three arguments: the namespace URI, the parameter local name, and the value to set. Typically, the namespace URI is null and the local name is simply the parameter's name. This method must be called prior to transformToDocument() or transformToFragment():
var oProcessor = new XSLTProcessor()
oProcessor.importStyleshee t(oXslDom) ;
oProcessor.setParameter(nu ll, "message", "Hello World!");
var oResultDom = oProcessor.transformToDocu ment(oXmlD om);
so in your case:
xsltProcessor = new XSLTProcessor();
xsltProcessor.importStyles heet(xsl);
xsltProcessor.setParameter (null, "parmName", "parm value");
resultDocument = xsltProcessor.transformToF ragment(pX ml, document);
The XSLTProcessor in Firefox also allows you to set XSLT parameters. The setParameter() method accepts three arguments: the namespace URI, the parameter local name, and the value to set. Typically, the namespace URI is null and the local name is simply the parameter's name. This method must be called prior to transformToDocument() or transformToFragment():
var oProcessor = new XSLTProcessor()
oProcessor.importStyleshee
oProcessor.setParameter(nu
var oResultDom = oProcessor.transformToDocu
so in your case:
xsltProcessor = new XSLTProcessor();
xsltProcessor.importStyles
xsltProcessor.setParameter
resultDocument = xsltProcessor.transformToF
ASKER
Ah thanks for this I appreciate it.
I've put this in my XSL
<xsl:param name="detail-id" >testvalue</xsl:param>
Im trying to get at it like this...
xsl = loadXMLDoc("cdcatalog.xsl" );
var paramNode=xsl.selectSingle Node("//xs l:param[na me=detail- id]")
alert(paramNode);
Will have a look at what you have provide right away.
Thanks.
I've put this in my XSL
<xsl:param name="detail-id" >testvalue</xsl:param>
Im trying to get at it like this...
xsl = loadXMLDoc("cdcatalog.xsl"
var paramNode=xsl.selectSingle
alert(paramNode);
Will have a look at what you have provide right away.
Thanks.
ASKER
Im fine with firefox, IE and Opera because I can easily use sarissa with these, no probs, all working there.. It's the webkit ones. I've seen that JQuery transform. After speaking to you guys I suppose I can just change set a value of xslt param through using DOM, as after all xsl will just be like an xml file. This is the direction I need to take, because then I can put my old code back with the breadcrumb that works fine and just use that.
So changing the param value by using DOM would mean I can go back to initial way without cutting out pieces of xml to get the desired result. Im nearly there.
I will definately pass on saome points to you.
Thanks.
So changing the param value by using DOM would mean I can go back to initial way without cutting out pieces of xml to get the desired result. Im nearly there.
I will definately pass on saome points to you.
Thanks.
Here is the code from that plugin in case you just want to see how to do it in IE and Fx
if($.browser.msie) {
var xml_X = ["MSXML4.DOMDocument", "MSXML3.DOMDocument", "MSXML2.DOMDocument", "MSXML.DOMDocument", "Microsoft.XmlDom"];
var objXML = false;
var objXSL = false;
for(var i = 0; i<xml_X.length;i++) {
try {
objXML = new ActiveXObject(xml_X[i]);
objXSL = new ActiveXObject(xml_X[i]);
i = xml_X.length;
} catch(ex) {
objXML = false;
objXSL = false;
}
}
objXSL.loadXML(o.c.xslstr);
objXML.loadXML(o.c.xmlstr);
var addparams = function(op, xObj) {
for(var p in op) {
var strParam = "//xsl:param[@name='" + p + "']";
try {
if(isNaN(parseInt(op[p])) && op[p].indexOf("'") < 0) {
op[p] = op[p].indexOf("\"") >= 0 ? op[p] = op[p].replace(/"/, "'") : op[p] = "'" + op[p] + "'";
};
var xslParam = xObj.selectSingleNode(strParam);
xslParam.setAttribute("select",op[p]);
} catch(ex) {
//param failed
}
}
};
addparams(o.c.xslParams,objXSL);
tel.html(objXML.transformNode(objXSL));
} else {
var parser = new DOMParser();
var processor = new XSLTProcessor();
var objXSL = parser.parseFromString(o.c.xslstr,"text/xml");
var objXML = parser.parseFromString(o.c.xmlstr,"text/xml");
var addparams = function(op) {
for(var p in op) {
processor.setParameter(null, p, op[p]);
}
};
addparams(o.c.xslParams);
processor.importStylesheet(objXSL);
var resultDoc = processor.transformToFragment(objXML, document);
tel.empty().append(resultDoc);
};
ASKER
<xsl:param name="detail-id" >yoyoyo</xsl:param>
This was works to a degree in the both webkit browsers.
var paramNode=xsl.selectSingle Node("//xs l:param[@n ame='detai l-id']")
var xmlString = new XMLSerializer().serializeT oString(pa ramNode);
alert(xmlString);
Have to bring it back to string as usual. It displays the whole xsl:param as a string. I just want the value which is as above "yoyoyo". Once I can isolate that then I can hopefully change it and put it back. This is the direction I am taking.
This was works to a degree in the both webkit browsers.
var paramNode=xsl.selectSingle
var xmlString = new XMLSerializer().serializeT
alert(xmlString);
Have to bring it back to string as usual. It displays the whole xsl:param as a string. I just want the value which is as above "yoyoyo". Once I can isolate that then I can hopefully change it and put it back. This is the direction I am taking.
ASKER
var values = paramNode.getAttribute("na me");
alert(values);
AS bove line gets the 'detail-id'. What I want is to print "yoyoyo" to screen. I'm assuming name/value pair. So...
var values = paramNode.getAttribute("va lue");
But no, it gives me null. Ah well, who needs logic anyway. Still hacking.
alert(values);
AS bove line gets the 'detail-id'. What I want is to print "yoyoyo" to screen. I'm assuming name/value pair. So...
var values = paramNode.getAttribute("va
But no, it gives me null. Ah well, who needs logic anyway. Still hacking.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Got there in the end.
xsl = loadXMLDoc("cdcatalog.xsl" );
var paramNode=xsl.selectSingle Node("//xs l:param[@n ame='detai l-id']")
var values = paramNode.childNodes(0).da ta = "I have been changed";
Yey, I did it like this. So now I can just revert back to my old code as if a parameter has been passed in. This will mean I don't need to cut up the xml, so the breadcrumb will work again. This time over all browsers, using .setParamter with firefox and IE. Would you say this is ok.
Thanks.
xsl = loadXMLDoc("cdcatalog.xsl"
var paramNode=xsl.selectSingle
var values = paramNode.childNodes(0).da
Yey, I did it like this. So now I can just revert back to my old code as if a parameter has been passed in. This will mean I don't need to cut up the xml, so the breadcrumb will work again. This time over all browsers, using .setParamter with firefox and IE. Would you say this is ok.
Thanks.
ASKER
I have evenly distributed. I hope this is ok.
welcome,
this sounds very much OK,
given that addParameter() doesn't work in webkit browsers (I still have to check on that),
this is exactly how I would have done this
I think you made the breadcrump XSLT code overly complex
You could actually iterate over ancestor axis with a simple recursive call and build up the breadcrump,
but that is just food for thought
Distribution is OK, don't worry.
I am happy Michel stepped in with his JavaScript expertise, mine is a bit rusty :-)
this sounds very much OK,
given that addParameter() doesn't work in webkit browsers (I still have to check on that),
this is exactly how I would have done this
I think you made the breadcrump XSLT code overly complex
You could actually iterate over ancestor axis with a simple recursive call and build up the breadcrump,
but that is just food for thought
Distribution is OK, don't worry.
I am happy Michel stepped in with his JavaScript expertise, mine is a bit rusty :-)
I still want to see how to insert an xsl:param from scratch :)
Your final solution worked because you already had one defined (cool too)
I will return if I get the answer
Your final solution worked because you already had one defined (cool too)
I will return if I get the answer
Well, it makes sense to have one in place and use that one.
That would have been my comment on your suggestion to create a new one.
If i had to develop the XSLT, I would like one in there allready, so I can test the stuff working outside the JavaScript.
The risk is that if you forget to remove it after development, you end up with to and the XSLT does not compile
That would have been my comment on your suggestion to create a new one.
If i had to develop the XSLT, I would like one in there allready, so I can test the stuff working outside the JavaScript.
The risk is that if you forget to remove it after development, you end up with to and the XSLT does not compile
Sure. But it is still cool to have in your arsenal
that is correct :-)
You cut out part of the XML, based on the @id
Doing that, of course you loos the context for building the breadcrump
I thought that all Webkit issues with Sarissa were fixed by now,
but have not tested enough to be sure
But there is another way to "pass" in a parameter
Instead of using addParameter, you could sneekily change the XSLT in place
It would definitely simplify the javascript A LOT
Just have an xsl:param the same way you would for adding a parameter
Then access that param in a DOM method, and change it for the correct id
(pretty much as you do with selectSingleNode)
This way the XML is passed on fully, so you can calculate the breadcrump easily
It is the XSLT that was changed prior to the transform
Just to be curious, are you sure that the addParameter is not working for sarissa/webkit?
Note that for parameter passing you need a template processor
As soon as breadcrumps are involved I see many people have both the breadcrump and the rest of the XSLT result in a div
Sarissa requires the transform result to be wellformed.
That could be biting you... just a thought