How to compare conetent with two tags using Xpath

Viewed 101

I have below scenarios of $html contents. I want to check if html content is start with a media (image or video, iframe) without any text content like 3rd scenario.

//no contetn between first p tag and image tag

   $html =  '<p dir="ltr"><img src="imageurl"  class="img"><br></p>
    <div>some content </div>';

//no content between first p tag and video tag

 $html =  '<p dir="ltr"><video width="320" height="240" controls>
  <source src="movie.mp4" type="video/mp4">
  <source src="movie.ogg" type="video/ogg">
  Your browser does not support the video tag.
</video></p>
<div>some content </div>';

//having content inside first p tag

 $html =  '<p dir="ltr">here we have text<img src="imageurl"  class="img"><br></p>
<div>some content </div>';

I have tried like this but no luck, please advice

$dom = new DOMDocument();
$dom->loadHTML($html);//that's HTML of my document, string
$xpath = new DOMXPath($dom);
$xpath_resultset =  $xpath->query("p/following-sibling::node()[not(preceding-sibling::img) and not(self::img)]");
2 Answers

Since p is de root node you need to add a slash at the start.

Check for p if first content is text() with text

/p[node()[1][self::text()][normalize-space()!='']]

Check for p if first content (ignoring whitespace) is img or video

/p[(video|img)[not(preceding-sibling::text()[normalize-space()!=''])]]

Both XPath's will ignore unintended white-space between the child-nodes.

So i.e. if p is like this:

<p dir="ltr"> <img src="imageurl"  class="img"/><br/></p>

The second XPath would find it, not the first one.

If you want to match all p's in a complete html-file you could use:

//p[node()[1][self::text()][normalize-space()!='']]

and

//p[(video|img)[not(preceding-sibling::text()[normalize-space()!=''])]]

It is not entirely clear which element you are trying to locate (the p, the img, or the video) or if you are just expecting the XPath to select nothing if it fails your requirement thus it doesn't matter what is selected when it meets the requirement.

If selecting the p is acceptable, then this kind of approach should work.

p[./((node() | text())[1][self::img or self::video])]

This selects the p if, among all of its element/text children, the first child is img or video.

If you need to select the first child (the img or video), then shifting the expression around a bit will do that:

p/(((node() | text())[1][self::img or self::video]))

returning the first element/text child of the p if it's an img/video element.

I hope it is self-explanatory how to modify this to include iframe (as mentioned in the original question).

Related