2012-02-14 5 views
1

J'utilise cette requête XPath pour sélectionner des éléments qui n'ont pas les descendants d'entrée dans un document Xhtml:Comment sélectionner uniquement les "nœuds supérieurs" dans cette requête XPath?

//*[not(descendant-or-self::input | descendant-or-self::textarea | descendant-or-self::select | ancestor::select)] 

Avec l'exemple suivant le document XHtml:

<html> 
    <head> 
     <title>Title</title> 
    </head> 
    <body> 
     <div id="one"> 
      <input type="text" /> 
     </div> 
     <div id="two"> 
      <textarea></textarea> 
     </div> 
     <div id="three"> 
      <div id="four"> 
       Text 
      </div> 
     </div> 
     <div id="five"> 
      <select> 
       <option>One</option> 
       <option>Two</option> 
      </select> 
     </div> 
     <div id="six"> 
      <input type="text" /> 
     </div> 
     <div id="seven"> 
      <div id="eight"></div> 
     </div> 
    </body> 
</html> 

... Et ce PHP Code:

// Populate $html and $query with above 

$dom = new DOMDocument('1.0', 'UTF-8'); 
$dom->loadXML($html); 

$xpath = new DOMXPath($dom); 
$nodes = $xpath->query($query); 

foreach($nodes as $node) 
{ 
    echo $node->tagName; 

    if($node->hasAttribute('id')) 
     echo '#' . $node->getAttribute('id'); 

    echo ' '; 
} 

Je reçois ceci: head title div#three div#four div#seven div#eight

Mais je veux à la place: head div#three div#seven

Je vais prendre les résultats de la requête XPath et supprimer les éléments de DOMDocument. title div#four div#eight sont des enfants de head div#three div#seven, qui sont déjà dans le résultat.

En gardant à l'esprit cette requête sera utilisée sur n'importe quel document XHtml, comment pourrais-je changer ma requête XPath 1.0 pour obtenir les résultats souhaités?

Répondre

1

répéter la condition pour le parent:

[not(descendant-or-self::input | descendant-or-self::textarea | descendant-or-self::select | ancestor-or-self::select) 
and 
(../descendant-or-self::input | ../descendant-or-self::textarea | ../descendant-or-self::select | ../ancestor-or-self::select)] 
+0

Cela fonctionne! Il semble qu'il suffise que la condition répétée soit 'et (../descendant::input | ../descendant::textarea | ../ descendant :: select)'. La partie 'ancestor-or-self :: select' de la première condition garantit que l'intérieur de' select' est laissé seul. La partie '../', si je comprends bien, sélectionne le "parent racine", ce qui est exactement ce que je voulais, merci :). J'ai également ajouté un repli pour sélectionner le noeud racine, 'html', s'il n'y a pas d'éléments de champ. – Luke

+0

J'ai découvert un problème avec la requête ne fonctionnait pas lorsque l'attribut 'xmlns' était présent sur l'élément' html'. Pour quelque raison que ce soit, vous ne pouvez pas simplement faire '$ xpath-> registerNamespace (NULL, 'http://www.w3.org/1999/xhtml')' ... Donc, vous devez remplacer 'NULL' par' ' 'html'' et faire '$ query = str_replace (' :: ',' :: input: ', $ query)' quand le xmlns est présent. Vous pouvez utiliser '$ xmlns = $ document-> lookupNamespaceURI (NULL)' pour voir s'il est présent. J'aimerais qu'il y ait un meilleur moyen. S'il y a, s'il vous plaît faites le moi savoir! – Luke

0

Dans ce cas, il semble être suffisant pour prolonger votre XPath actuelle de dire plus qu'il ne devrait pas être un div ou un head dans l'axe ancestor:

//*[not(descendant-or-self::input 
     | descendant-or-self::textarea 
     | descendant-or-self::select 
     | ancestor::select 
     | ancestor::div 
     | ancestor::head)] 

Sur l'échantillon xml, retourne seulement les head et div s three et seven, selon les besoins.

+0

Malheureusement, cela ne fonctionne pas pour tous les documents XHtml/Html5. C'est trop spécifique. Je ne peux être précis sur les champs de formulaire. Il y a des éléments en bloc/en ligne qui peuvent contenir un champ de formulaire qui devrait être listé ici. Manquer l'un d'entre eux pourrait entraîner une erreur (en essayant de retirer une étiquette dont le parent a déjà été retiré). – Luke

Questions connexes