使用PHP中的DOMDocument在h3标记集之间包装所有HTML标记

时间:2012-05-22 13:29:13

标签: php html domdocument

我有一个问题的跟进问题,杰克已经回答了这个问题: Wrap segments of HTML with divs (and generate table of contents from HTML-tags) with PHP

我一直在努力为上面的答案添加一些功能,以获得以下结果。

这是我目前的HTML:

<h3>Subtitle</h3>
<p>This is a paragraph</p>
<p>This is another paragraph</p>
<h3>Another subtile
  <h3>
    <p>Yet another paragraph</p>

这就是我想要实现的目标:

<h3 class="current">Subtitle</h3>
<div class="ac_pane" style="display:block;">
  <p>This is a paragraph</p>
  <p>This is another paragraph</p>
</div>
<h3>Another subtitle</h3>
<div class="ac_pane">
  <p>Yet another paragraph</p>
</div>

我一直在尝试修改上面示例中的代码,但无法弄清楚:

foreach ($d->getElementsByTagName('h3') as $h3) {
    $ac_pane_nodes = array($h3);
    for ($next = $h3->nextSibling; $next && $next->nodeName != 'h3'; $next = $next->nextSibling) {
        $ac_pane_nodes[] = $next;
    }
    $ac_pane = $d->createElement('div');
    $ac_pane->setAttribute('class', 'ac_pane');
    // Here I'm trying to wrap all tags between h3-sets, but am failing!
            $h3->parentNode->appendChild($ac_pane, $h3);
    foreach ($ac_pane_nodes as $node) {
        $ac_pane->appendChild($node);
    }
}

请注意,向第一个h3集添加class="current"以及向第一个style="display:block;"添加div.ac_pane是可选的,但我们非常感谢。

1 个答案:

答案 0 :(得分:4)

根据要求,这是一个工作版本。 IMO XSLT仍然是最适合此类问题的解决方案(实际上将一些XML转换为其他XML)但我不得不承认使用常规代码进行分组更容易!

我最终只是稍微扩展了DOM API,只是为了在DOMElement上添加一个实用程序insertAfter方法。没有它可以做到,但它更整洁:

根据评论中所要求的所有标签更新绕过DIV

<?php

class DOMDocumentExtended extends DOMDocument {
    public function __construct($version = "1.0", $encoding = "UTF-8") {
        parent::__construct($version, $encoding);
        $this->registerNodeClass("DOMElement", "DOMElementExtended");
    }
}

class DOMElementExtended extends DOMElement {
    public function insertAfter($targetNode) {
        if ($targetNode->nextSibling) {
            $targetNode->parentNode->insertBefore($this, $targetNode->nextSibling);
        } else {
            $targetNode->parentNode->appendChild($this);
        }
    }

    public function wrapAround(DOMNodeList $nodeList) {
        while (($node = $nodeList->item(0)) !== NULL) {
            $this->appendChild($node);
        }
    }
}

$doc = new DOMDocumentExtended();
$doc->loadHTML(
    "<h3>Subtitle</h3>
    <p>This is a paragraph</p>
    <p>This is another paragraph</p>
    <h3>Another subtile</h3>
    <p>Yet another paragraph</p>"
);

// Grab a nodelist of all h3 tags
$nodeList = $doc->getElementsByTagName("h3");

// Iterate over each of these h3 nodes
foreach ($nodeList as $index => $h3) {

    // Special handling for first h3
    if ($index === 0) {
        $h3->setAttribute("class", "current");
    }

    // Create a div node that we'll use as our wrapper
    $div = $doc->createElement("div");
    $div->setAttribute("class", "ac_pane");

    // Special handling for first div wrapper
    if ($index === 0) {
        $div->setAttribute("style", "display:block;");
    }

    // Move next siblings of h3 until we hit another h3
    while ($h3->nextSibling && $h3->nextSibling->localName !== "h3") {
        $div->appendChild($h3->nextSibling);
    }

    // Add the div node right after the h3
    $div->insertAfter($h3);
}

// UPDATE: wrap all child nodes of body in a div
$div = $doc->createElement("div");
$body = $doc->getElementsByTagName("body")->item(0);
$div->wrapAround($body->childNodes);
$body->appendChild($div);

echo $doc->saveHTML();

请注意,loadHTML将添加doctype,html和body节点。 They can be stripped out if needed