Viewed   611 times

I'm using PHP DOM and I'm trying to get an element within a DOM node that have a given class name. What's the best way to get that sub-element?

Update: I ended up using Mechanize for PHP which was much easier to work with.

 Answers

2

Update: Xpath version of *[@class~='my-class'] css selector

So after my comment below in response to hakre's comment, I got curious and looked into the code behind Zend_Dom_Query. It looks like the above selector is compiled to the following xpath (untested):

[contains(concat(' ', normalize-space(@class), ' '), ' my-class ')]

So the PHP would be:

$dom = new DomDocument();
$dom->load($filePath);
$finder = new DomXPath($dom);
$classname="my-class";
$nodes = $finder->query("//*[contains(concat(' ', normalize-space(@class), ' '), ' $classname ')]");

Basically, all we do here is normalize the class attribute so that even a single class is bounded by spaces, and the complete class list is bounded in spaces. Then append the class we are searching for with a space. This way we are effectively looking for and find only instances of my-class .


Use an xpath selector?

$dom = new DomDocument();
$dom->load($filePath);
$finder = new DomXPath($dom);
$classname="my-class";
$nodes = $finder->query("//*[contains(@class, '$classname')]");

If it is only ever one type of element you can replace the * with the particular tagname.

If you need to do a lot of this with very complex selector I would recommend Zend_Dom_Query which supports CSS selector syntax (a la jQuery):

$finder = new Zend_Dom_Query($html);
$classname = 'my-class';
$nodes = $finder->query("*[class~="$classname"]");
Friday, November 4, 2022
2

Using simpleXML:

$xml = new SimpleXMLElement($xmlstr);
echo $xml->file['path']."n";

Output:

http://www.thesite.com/download/eysjkss.zip
Friday, August 26, 2022
5

You can use XPath on your DOMDocument as follows:

$doc->loadHTML($article_header);
$xpath = new DOMXpath($doc);

$imagesAndIframes = $xpath->query('//img | //iframe');

$length = $imagesAndIframes->length;
for ($i = 0; $i < $length; $i++) {
    $element = $imagesAndIframes->item($i);

    if ($element->tagName == 'img') {
        echo 'img';
    } else {
        echo 'iframe';
    }
}
Monday, October 17, 2022
 
sinned
 
5

The problem is that the NodeList returned to you is "live" - it changes as you alter the class name. That is, when you change the class on the first element, the list is immediately one element shorter than it was.

Try this:

  while (elementArray.length) {
    elementArray[0].className = "exampleClassComplete";
  }

(There's no need to use setAttribute() to set the "class" value - just update the "className" property. Using setAttribute() in old versions of IE wouldn't work anyway.)

Alternatively, convert your NodeList to a plain array, and then use your indexed iteration:

  elementArray = [].slice.call(elementArray, 0);
  for (var i = 0; i < elementArray.length; ++i)
    elementArray[i].className = "whatever";

As pointed out in a comment, this has the advantage of not relying on the semantics of NodeList objects. (Note also, thanks again to a comment, that if you need this to work in older versions of Internet Explorer, you'd have to write an explicit loop to copy element references from the NodeList to an array.)

Sunday, August 28, 2022
 
5

This works

div {
    display:table;
}
input {
    display:table-footer-group;
}
label {
    display:table-header-group;
}

http://jsfiddle.net/v8xTC/

Friday, December 2, 2022
 
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :