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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用PHP中的DOMDocument在h3标记集之间包装所有HTML标记相关的知识,希望对你有一定的参考价值。

我对杰克回答的问题有一个跟进问题: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);
    }
}

请注意,将class="current"添加到第一个h3组,并将style="display:block;"添加到第一个div.ac_pane是可选的,但非常感谢。

答案

根据要求,这是一个工作版本。 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

以上是关于使用PHP中的DOMDocument在h3标记集之间包装所有HTML标记的主要内容,如果未能解决你的问题,请参考以下文章

DOMDocument::loadHTML 错误

通过选择标记名称在PHP中创建属性

这是 PHP 的 DOMDocument 库中的错误吗?

PHP 4 中的新 DOMDocument()

如何使用 DOMDocument 编写 XML 自闭标签

URL 替换我内容中 <h3> 标记值的 .php?id=1 扩展名