Почему DomDocument getElementsByTagName возвращает половину NodeList?

Я генерирую HTML-код нестандартного тега с помощью DomDocument, и в результате получается следующее:

/* Input HTML <div id="toobar_top"> <widget id="flag_holder"></widget> <widget id="horizontal_menu"></widget> </div> <div id="header"> <widget name="header"></widget> </div> */ 

То, что я хочу сделать, это «перевести» каждый виджет в нечто полезное … они простые заполнители с параметрами.

Выделение функции из класса:

 private function widgeter($doc) { //Give it an DomDocument HTML containing <widget> elements and will translate them into usable stuff $this->_widgetList = $doc->getElementsByTagName($this->_widgetTransformTo); foreach ($this->_widgetList as $widget) { $data = array(); if ($widget->hasAttributes()) { foreach ($widget->attributes as $attribute) { $data[][$attribute->name] = $attribute->value; // @TODO: Implements Widget Transformation } } // Next 2 lines are just for debug $string = serialize($data); $newWidget = $doc->createElement('p', $string); $widget->parentNode->replaceChild($newWidget, $widget); } return $doc; } 

тогда, когда я сохраняюHTML () $ doc, я вижу:

 /* Output HTML <div id="toobar_top"> <p>[{"id":"flag_holder"}]</p> <widget id="horizontal_menu"></widget> </div> <div id="header"> <p>[{"id":"header"}]</p> </div> */ 

почему «horizontal_menu» не был переведен?

Неважно, где виджеты (я пробовал только один div со всеми виджетами и с div на виджет).

Я не могу понять это …

Solutions Collecting From Web of "Почему DomDocument getElementsByTagName возвращает половину NodeList?"

Это происходит из-за того, что вы заменяете элементы в DOMNodeList, когда они зацикливаются на них. DOMNodeList не является массивом , поэтому foreach не работает на копии , а на самом объекте.

В основном, я думаю, что происходит:

  • Вы заменяете первый экземпляр <widget> (Item 0).
  • Указатель переходит к следующему пункту (пункт 1).
  • Пункт 0 был заменен и больше не существует.
  • Перемещение предмета происходит: Пункт 1 становится Item 0, Item 2 становится Item 1.
  • Указатель по-прежнему указывает на Item 1 (который первоначально был Item 2, фактически пропуская узел).

Что вам нужно сделать, так это сохранить элементы в массиве, а затем изменить их, а не зацикливать на DOMNodeList:

 $this->_widgetList = array(); foreach ($domNodeList as $node) { $this->_widgetList[] = $node; } foreach ($this->_widgetList as $widget) { // do stuff } 

чтобы избежать двух итераций, вы можете проанализировать список элементов, обратных

 $widgets = $doc->getElementsByTagName( 'widget' ); // get all elements for( $i = $widget->length; $i > 0; $i-- ){ $widget = $doc->getElementsByTagName( 'widget' )->item( $i - 1 ); // do stuff whith the widget }