views:

4135

answers:

11

I'm trying to select a specific HTML element in a document, for firefox i just use:

xpathobj = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);

which works fine. However when i try the IE equivilent:

xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async=false;
xmlDoc.load(document);
xmlDoc.setProperty("SelectionLanguage", "XPath");
xpathobj = xmlDoc.selectNodes(xpath);

I get no object returned. So my question is there an easy way to use xpath to get to the element i want in IE? The xpath i'm using looks like "/HTML/BODY/DIV[9]/DIV[2]"

A: 

Are you sure X-Path is implemented in your version of Internet Explorer? As in: what version are you using?

roosteronacid
A: 

I'm using IE6

+2  A: 

Take a look at http://dev.abiss.gr/sarissa/ project. They have migrated most of XML-related APIs to IE. Otherwise it is indeed also easy to implement. The problems you would need to solve would be: serialization of HTML into valid XML, syncing result of XMLDOM XPath query with original HTMLDOM. To my knowledge they've done it in their library, however, its performance could have been better.

Sergey Ilinsky
A: 

jQuery implements a cross-browser-compatible subset of xPath selectors with a plug-in. Your example "/HTML/BODY/DIV[9]/DIV[2]" should work in it.

(edit - corrected thanks to Sergey Ilinsky)

Neall
jQuery DOES NOT implement XPath. It implements a very simple path-selecting feature (that is about ~0.01% of XPath)
Sergey Ilinsky
A: 

Instead of doing

xmlDoc.load(document);

try

xmlDoc.loadXML(document.body.outerHTML)

This would only really work if your your HTML document is formatted to XHTML standards. Also, the BODY tag would be the root node, so you would have to change your XPATH to "/BODY/DIV[9]/DIV[2]"

Tim C
A: 

I'd be a bit worried about using xml like this, as you cannot be sure what version (if any) of the XML DLL a person has. There are still companies using IE5.0 out there in droves, and 5.5 had a particularly ropey XML implementation.

Magnus Smith
A: 

Hi well in the end i came up with my own dodgy solution, any sugestions on improving it would be greatly apreciated. It makes use of some prototype functionality:

Works in IE5+ with xpath of the form "/HTML/BODY/DIV[9]/DIV[2]"

function getXPathElement (xpath , element) {

//Specific to project, here i know that the body element will always have the id "top"
//but otherwise the element that is passed in should be first element in the xpath 
//statement eg. /HTML/BODY/DIV the element passed in should be HTML
if(!element){
 element = $("top");
 var xpathArrayIndex = 3;
} else {
 var xpathArrayIndex = 1;
}
//split the xpath statement up
var xpathArray = xpath.split("/");

var carryOn = true; 
while(carryOn){
 decendents = element.childElements();
 //check to see if we are at the end of the xpath statement
 if(xpathArrayIndex == xpathArray.length){
  return element;
 }
 //if there is only one decendent make it the next element
 if(decendents.size() == 1) {
  element = decendents.first();
 } else {
 //otherwise search the decendents for the next element
  element = getXPathElementByIndex(decendents, xpathArray[xpathArrayIndex]);
 }
 xpathArrayIndex++;
}

}

function getXPathElementByIndex(decendents, xpathSegment){

var decendentsArray = decendents.toArray();
//seperate the index from the element name
var temp = xpathSegment.split("[");
var elementName = temp[0];
//get the index as a number eg. "9]" to 9
var elementIndex = +temp[1].replace("]", "");
//the number of matching elements
var count = 0;

//keeps track of the number of iterations
var i = 0;
while(count != elementIndex) {
 //if the decendent's name matches the xpath element name increment the count
 if(decendentsArray[i].nodeName == elementName){
  count++;
 }
 i++;
}
var element = decendentsArray[i - 1];
return element;

}

Thanks to everyone for thier help, either way i learnt a fair bit about various javascript frameworks.

A: 

Another JavaScript implementation of W3C Dom Level 3 XPath can be found on Source Forge. But does not appear to be active.

Richard
+2  A: 

The problem may be that in IE5+ [1] is in fact [2] in FF. Microsoft solely decided that the numbering should start at [0] and not [1] as specified by w3c.

Koder
A: 

I can not find a simple and common solution, you can write custom function to implement a little of xpath but it is hard to get complete in internet explorer 6 or lower version....

hyperion
A: 

There are some bugs in oly1234's code I try to fix it as follows:

function getXPathElement(xpath, element){
if(!element){
    element = document;
}
var xpathArray = xpath.split("/");

element = findXPathRoot(xpathArray[0],xpathArray[1],element);

for(var i=1; i<xpathArray.length; i++){
    if(xpathArray[i].toLowerCase()=="html"){
        continue;
    }
    if(!element){
        return element;
    }
    element = getXPathElementByIndex(element.childNodes,xpathArray[i]);         
}
return element;
}


function findXPathRoot(rootPath,htmlPath,element){
if(rootPath == ""&&htmlPath.toLowerCase() == "html"){
    return element.documentElement;
}
return document.getElementsByTagName(rootPath)[0];
}
function getXPathElementByIndex(decendents, xpathSegment){
//seperate the index from the element name
var temp = xpathSegment.split("[");
var elementName = temp[0];
//get the index as a number eg. "9]" to 9
if(temp[1]){
    var elementIndex = temp[1].replace("]", "");
}else{
    var elementIndex = 1;
}
//the number of matching elements
var count = 0;
for(var i=0;i < decendents.length; i++){
    if (decendents[i].nodeName.toLowerCase() == elementName.toLowerCase()) {
        count++;
        if(count==elementIndex){
            return decendents[i];
        }
    }
}
return null;
}
hyperion