2016-04-28 11 views
4

Ich versuche p Tags mit data-spotid AttributeDomDocument removeChild in foreach reindexing den dom

 $dom = new DOMDocument(); 
     @$dom->loadHTML($description); 
     $pTag = $dom->getElementsByTagName('p'); 

     foreach ($pTag as $value) { 
      /** @var DOMElement $value */ 
      $id = $value->getAttribute('data-spotid'); 
      if ($id) { 
       $value->parentNode->removeChild($value); 
      } 
     } 

aber zu löschen, wenn ich Kind bin Entfernen sie den dom sind reindexing. Ich nehme an, ich habe 8 Items, die ich gelöscht habe, 1. es wird es neu indizieren und das zweite Element wird 1. und es wird nicht gelöscht, es wird auf 2. gehen, das jetzt 3. Element ist.

+1

Ich glaube nicht, "Reindex" ist der richtige Begriff. Es hört sich so an, als ob der Foreach-Iterator nicht in der Lage ist, sich selbst sozusagen zurückzuspulen, um immer auf dem aktuellsten Punkt in der Schleife zu sein, wenn diese Schleife manipuliert wird. Es könnte also ein allgemeineres Bezugsproblem sein als etwas, das für "DomDocument" spezifisch ist. – Anthony

+0

Scheint, als ob mein Vorgefühl etwas Verdienst hat: http://php.net/manual/en/domnode.removechild.php#90292 – Anthony

+0

Sie können ['iterator_to_array ($ pTag)'] (https: //secure.php. net/manual/function.iterator-to-array.php). Demo: https://3v4l.org/ieN3X – Yoshi

Antwort

2

Dies wird in ein paar Kommentare auf der DomNode::removeChild Dokumentation erwähnt, mit der Frage offensichtlich sein, wie der Iterator Zeiger auf dem foreach nicht in der Lage zu sein mit der Tatsache zu tun dass Sie Elemente aus einem übergeordneten Array entfernen, während Sie die Liste der untergeordneten Elemente (oder etwas) durchlaufen.

Die empfohlene Lösung besteht darin, zunächst den Hauptknoten durchzulaufen und die untergeordneten Knoten, die Sie löschen möchten, in ein eigenes Array zu verschieben, dann das zu löschende Array zu durchlaufen und diese untergeordneten Elemente zu löschen. Beispiel:

$dom = new DOMDocument(); 
@$dom->loadHTML($description); 
$pTag = $dom->getElementsByTagName('p'); 

$spotid_children = array(); 

foreach ($pTag as $value) { 
    /** @var DOMElement $value */ 
    $id = $value->getAttribute('data-spotid'); 
    if ($id) { 
     $spotid_children[] = $value; 
    } 
} 

foreach ($spotid_children as $spotid_child) { 
    $spotid_child->parentNode->removeChild($spotid_child); 
} 
1

können wir wie folgt verwenden:

 $dom = new DOMDocument(); 
     @$dom->loadHTML($description); 
     $pTag = $dom->getElementsByTagName('p'); 
     $count = count($pTag) 
     for($i = 0; $i < $count; $i++) { 
      /** @var DOMElement $value */ 
      $value = $pTag[$i]; 
      $id = $value->getAttribute('data-spotid'); 
      if ($id) { 
       $i--;$count--; 
       $value->parentNode->removeChild($value); 
      } 
     } 
1

Wie ich bemerkte, die leicht Lösung wäre, nur werfen den Iterator auf ein Array. Beispiel:

Aber wenn wir über die Leistung sprechen, wäre ein besserer Weg, einfach nur die erforderlichen Knoten auszuwählen. Eine ordentliche Nebenwirkung, das Entfernungsproblem verschwindet ebenfalls.

Z. B .:

<?php 
$doc = new DOMDocument('1.0', 'UTF-8'); 
$doc->loadXML(<<<__XML 
<?xml version="1.0" encoding="UTF-8"?> 
<root> 
    <element>1</element> 
    <element attr="a">2</element> 
    <element>3</element> 
    <element>4</element> 
    <element attr="a">5</element> 
    <element attr="a">6</element> 
    <element>7</element> 
    <element>8</element> 
</root> 
__XML 
); 

$xpath = new DOMXPath($doc); 
$elements = $xpath->query('//element[@attr]'); 

foreach ($elements as $element) { 
    $element->parentNode->removeChild($element); 
} 

echo $doc->saveXML(); 

Demo: https://3v4l.org/CM9Fv

+0

Dies ist eine solide Lösung, erfordert aber zwei Dinge, die möglicherweise nicht möglich sind: 1) dass die Zielelemente über XPath ausgewählt werden können (dies ist möglicherweise nicht der Fall, wenn Die Elemente, die entfernt werden müssen, basieren auf verrückter Logik, wie "ob Kindelementattribut1 = 'x' und Attribut2> = 7" oder schlechter) und 2) der Aufwand, um herauszufinden, ob ein XPath funktioniert derjenige, der (wenn es einen gibt) ist realistisch im Vergleich zu anderen Lösungen. – Anthony

0

(Unter der Annahme, dass der $ dom den (DOM enthält) Die Absätze müssen Sie herausfiltern). Lassen Sie uns einige gute alte JavaScript versuchen:

$ptag = $dom.all.tags("p"); 
$ptag = [].slice.call($ptag); 
$i = 0; 
while($ptag[$i]){ 
'data-spotid' in $ptag[$i].attributes ? $ptag[$i++].outerHTML = "" : 0 
} 

HINWEIS: I Outerhtml bin mit unerwünschten Elemente zu zerstören, um seine Eltern zu vermeiden Aufruf und verlagern Sie den Knoten von Interesse, das wir bereits haben. Aktuelle Firefox-Versionen unterstützen es endlich (11+). MDN ref

Ich verwende auch die kurze all.tags() Syntax für die Kürze; Firefox unterstützt es vielleicht noch nicht, also sollten Sie vielleicht wieder auf den Aufruf 'getElementsByTagName()' zurückgreifen.

+1

Um Ihr Problem vollständig zu entmystifizieren, müssen wir die Tatsache offenlegen, dass Sie an einer Live-Sammlung arbeiten. Durch das Durchschleifen von Elementen aus einer Live-Dom-Sammlung mit linear wachsendem Index wird es zweifellos dazu führen, dass Elemente übersprungen werden. Deshalb müssen Sie es ($ ptag live collection) in Static konvertieren. Das habe ich dort gemacht. –