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.
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)
}

Open in new window

LizHelpMeAsked:
Who is Participating?
 
Michel PlungjanConnect With a Mentor IT ExpertCommented:
play with this

xsl = loadXMLDoc("cdcatalog.xsl");

var xslparam = document.createElement('xsl:param')
xslparam.setAttribute("name","parm1")
xslparam.setAttribute("value","parameter 1")
xsl.documentElement.appendChild(xslparam,xsl.documentElement.firstChild);

I do not have a good example and am not sure I add it to the right node
0
 
Geert BormansInformation ArchitectCommented:
Well, I see your problem (I think)

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
0
 
LizHelpMeAuthor Commented:
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("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);
}

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.
0
Cloud Class® Course: MCSA MCSE Windows Server 2012

This course teaches how to install and configure Windows Server 2012 R2.  It is the first step on your path to becoming a Microsoft Certified Solutions Expert (MCSE).

 
LizHelpMeAuthor Commented:
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.
0
 
Geert BormansInformation ArchitectCommented:
Well, for myself, I would definitely prefer changing the parameter using the DOM way and get rid of all the navigation code in JavaScript.
0
 
LizHelpMeAuthor Commented:
Could you show me how to do it? Thanks.
0
 
Geert BormansInformation ArchitectCommented:
have to get something out for a customer this afternoon, will work on this later today
0
 
LizHelpMeAuthor Commented:
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.
<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>

Open in new window

0
 
LizHelpMeAuthor Commented:
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.
0
 
Michel PlungjanIT ExpertCommented:
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.importStylesheet(oXslDom);
    oProcessor.setParameter(null, "message", "Hello World!");
    var oResultDom = oProcessor.transformToDocument(oXmlDom);

so in your case:

  xsltProcessor = new XSLTProcessor();
  xsltProcessor.importStylesheet(xsl);
  xsltProcessor.setParameter(null, "parmName", "parm value");
  resultDocument = xsltProcessor.transformToFragment(pXml, document);
0
 
LizHelpMeAuthor Commented:
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.selectSingleNode("//xsl:param[name=detail-id]")
    alert(paramNode);

Will have a look at what you have provide right away.

Thanks.
0
 
LizHelpMeAuthor Commented:
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.
0
 
Michel PlungjanIT ExpertCommented:
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);
};

Open in new window

0
 
LizHelpMeAuthor Commented:
<xsl:param name="detail-id" >yoyoyo</xsl:param>

This was works to a degree in the both webkit browsers.

var paramNode=xsl.selectSingleNode("//xsl:param[@name='detail-id']")
    var xmlString = new XMLSerializer().serializeToString(paramNode);
    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.
0
 
LizHelpMeAuthor Commented:
var values = paramNode.getAttribute("name");
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("value");

But no, it gives me null. Ah well, who needs logic anyway. Still hacking.
0
 
Geert BormansConnect With a Mentor Information ArchitectCommented:
I think you will need a .nodeValue
var values = paramNode.getAttribute("name").nodeValue

set paramNode.getAttribute("name").nodeValue = "yoyoyo"

could work

need some further thinking but that would be close
0
 
LizHelpMeAuthor Commented:
Got there in the end.

xsl = loadXMLDoc("cdcatalog.xsl");
var paramNode=xsl.selectSingleNode("//xsl:param[@name='detail-id']")    
var values = paramNode.childNodes(0).data = "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.
0
 
LizHelpMeAuthor Commented:
I have evenly distributed. I hope this is ok.
0
 
Geert BormansInformation ArchitectCommented:
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 :-)
0
 
Michel PlungjanIT ExpertCommented:
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
0
 
Geert BormansInformation ArchitectCommented:
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
0
 
Michel PlungjanIT ExpertCommented:
Sure. But it is still cool to have in your arsenal
0
 
Geert BormansInformation ArchitectCommented:
that is correct :-)
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.