为什么DomDocument getElementsByTagName 返回一半的NodeList?

发布于 2024-10-28 06:01:49 字数 1623 浏览 2 评论 0原文

我使用 DomDocument 生成一些非标准标签 HTML,结果是这样的:

/* 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;
    }

然后当我 saveHTML() 时,我看到 $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>
*/

为什么“horizo​​ntal_menu”没有翻译?

小部件在哪里并不重要(我尝试只使用一个 div,所有小部件都在其中,并且每个小部件都有一个 div)。

我想不通...

I generate some non-standard-tag HTML with DomDocument and the result is this:

/* 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>
*/

What I want to do is to "translate" each widget in something useful... they are simple placeholders with params.

The function extract from the class is:

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;
    }

then when I saveHTML() the $doc I see:

/* 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>
*/

why "horizontal_menu" wasn't translated?

It doesn't matter where widgets are (I tried with only one div with all widgets in and with a div per widget).

I can't figure out it...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

蔚蓝源自深海 2024-11-04 06:01:49

发生这种情况是因为您在循环遍历 DOMNodeList 中的元素时替换了这些元素。 DOMNodeList 不是数组,因此 foreach 不会对副本进行操作,而是对对象本身进行操作。

基本上,我认为正在发生的事情是:

  • 您替换 (Item 0) 的第一个实例。
  • 指针前进到下一个项目(项目 1)。
  • 项目 0 已被替换并且不再存在。
  • 发生项目移位:项目 1 变为项目 0,项目 2 变为项目 1。
  • 指针仍指向项目 1(原来是项目 2,有效地跳过节点)。

您需要做的是将元素保存在数组中,然后更改它们,而不是在 DOMNodeList 上循环:

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

foreach ($this->_widgetList as $widget) {
   // do stuff
}

It happens because you are replacing the elements in the DOMNodeList while looping on them. A DOMNodeList is not an array, so foreach does not operate on a copy, but on the object itself.

Basically, what I think is happening is:

  • You replace the first instance of <widget> (Item 0).
  • The pointer advances to the next item (Item 1).
  • Item 0 has been replaced and does not exist anymore.
  • Item shifting occurs: Item 1 becomes Item 0, Item 2 becomes Item 1.
  • The pointer still points to Item 1 (which was originally Item 2, effectively skipping a node).

What you need to do is to save the elements in an array and then change them, instead of looping on the DOMNodeList:

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

foreach ($this->_widgetList as $widget) {
   // do stuff
}
尘曦 2024-11-04 06:01:49

为了避免两次迭代,您可以逆向解析元素列表

$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
}

to avoid two iterations you can parse the list of elements inverse

$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
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文