1

我已经使用 DomDocument 来 GetElementById。它选择了一个div。我需要用 h4 标签替换该 div 中的所有标题标签。

4

1 回答 1

8

您在问题中没有明确说明您遇到的具体问题是什么。我假设有两个部分可能会给你一些问号。

第一个是如何掌握要重命名的所有元素,第二个实际上是如何重命名一个元素。

获取 DOMDocument 的标题元素

所以首先要做的事情是:要选择所有标题元素,您需要选择所有作为标题元素的标签 (h1 to h6)。结合它们还需要是具有特定 id 属性的 div 标记的子级的条件,这似乎是一件相当复杂的事情。但是,对于 xpath 查询,它仍然只是简单的。

作为我的代码示例的示例,我选择了 id `"content" 并且以下 xpath 表达式查询所有标题元素:

(
    //div[@id="content"]//h1
    |//div[@id="content"]//h2
    |//div[@id="content"]//h3
    |//div[@id="content"]//h4
    |//div[@id="content"]//h5
    |//div[@id="content"]//h6
)

如果我在这个网站上运行它(在我回答之前),它会创建以下标签列表:

Found 8 elements:
 #00: <h1>
 #01: <h2>
 #02: <h2>
 #03: <h3>
 #04: <h3>
 #05: <h3>
 #06: <h2>
 #07: <h4>

正如这很好地展示的那样,使用 xpath 查询,甚至可以创建不同元素的列表以及具有特定条件(例如作为具有 id 的 div 的子级)。这段代码一目了然:

$url = 'http://stackoverflow.com/questions/16307103/use-domdocument-to-replace-all-header-tags-with-the-h4-tags';

$dom = new DOMDocument();
$internalErrorsState = libxml_use_internal_errors(true);
$dom->loadHTMLFile($url);
libxml_use_internal_errors($internalErrorsState);
$xpath = new DOMXPath($dom);

$expression = '
(
    //div[@id="content"]//h1
    |//div[@id="content"]//h2
    |//div[@id="content"]//h3
    |//div[@id="content"]//h4
    |//div[@id="content"]//h5
    |//div[@id="content"]//h6
)';

$elements = $xpath->query($expression);
echo "Found ", $elements->length, " elements:\n";
foreach ($elements as $index => $element) {
    printf(" #%02d: <%s>\n", $index, $element->tagName);
}

重命名 DOMElement

那么关于重命名元素的第二个问题呢?

开箱即用的 DOMDocumet 不支持此功能。有一个方法存根(DOMDocument::renameNode(); 在当前的 PHP 手册中没有记录),但是如果你调用它,你会得到一个警告,它没有实现:

警告:DOMDocument::renameNode():尚未实现

相反,需要推出自己的版本。这就是它的工作原理:由于您无法使用 DOMDocument 重命名元素,您所能做的就是使用重命名的名称创建一个新元素并复制节点以重命名其所有属性和子节点,然后将其替换为重命名为浅拷贝。这是通过以下方法完成的:

/**
 * Renames a node in a DOM Document.
 *
 * @param DOMElement $node
 * @param string     $name
 *
 * @return DOMNode
 */
function dom_rename_element(DOMElement $node, $name) {
    $renamed = $node->ownerDocument->createElement($name);

    foreach ($node->attributes as $attribute) {
        $renamed->setAttribute($attribute->nodeName, $attribute->nodeValue);
    }

    while ($node->firstChild) {
        $renamed->appendChild($node->firstChild);
    }

    return $node->parentNode->replaceChild($renamed, $node);
}

将它与上面的循环结合起来,在foreach输出标记名旁边,它们也可以重命名:

$elements = $xpath->query($expression);
echo "Found ", $elements->length, " elements:\n";
foreach ($elements as $index => $element) {
    printf(" #%02d: <%s>\n", $index, $element->tagName);
    dom_rename_element($element, 'h4');
    ###################################
}

然后,再次查询 xpath 表达式,将只得到 h4 标签:

$elements = $xpath->query($expression);
echo "Found ", $elements->length, " elements:\n";
foreach ($elements as $index => $element) {
    printf(" #%02d: <%s>\n", $index, $element->tagName);
}

输出:

Found 8 elements:
 #00: <h1>
 #01: <h2>
 #02: <h2>
 #03: <h3>
 #04: <h3>
 #05: <h3>
 #06: <h2>
 #07: <h4>

完整代码示例

这里是完整的代码示例及其输出一目了然:

<?php
/**
 * Use DomDocument to replace all header tags with the h4 tags
 * @link http://stackoverflow.com/q/16307103/367456
 */
$url = 'http://stackoverflow.com/questions/16307103/use-domdocument-to-replace-all-header-tags-with-the-h4-tags';

$dom = new DOMDocument();
$internalErrorsState = libxml_use_internal_errors(true);
$dom->loadHTMLFile($url);
libxml_use_internal_errors($internalErrorsState);
$xpath = new DOMXPath($dom);

$expression = '
(
    //div[@id="content"]//h1
    |//div[@id="content"]//h2
    |//div[@id="content"]//h3
    |//div[@id="content"]//h4
    |//div[@id="content"]//h5
    |//div[@id="content"]//h6
)';

$elements = $xpath->query($expression);
echo "Found ", $elements->length, " elements:\n";
foreach ($elements as $index => $element) {
    printf(" #%02d: <%s>\n", $index, $element->tagName);
    dom_rename_element($element, 'h4');
}

$elements = $xpath->query($expression);
echo "Found ", $elements->length, " elements:\n";
foreach ($elements as $index => $element) {
    printf(" #%02d: <%s>\n", $index, $element->tagName);
}

/**
 * Renames a node in a DOM Document.
 *
 * @param DOMElement $node
 * @param string     $name
 *
 * @return DOMNode
 */
function dom_rename_element(DOMElement $node, $name) {
    $renamed = $node->ownerDocument->createElement($name);

    foreach ($node->attributes as $attribute) {
        $renamed->setAttribute($attribute->nodeName, $attribute->nodeValue);
    }

    while ($node->firstChild) {
        $renamed->appendChild($node->firstChild);
    }

    return $node->parentNode->replaceChild($renamed, $node);
}

如果您尝试一下,您可能会注意到,在我回答之后,标题元素的数量已经发生了变化。我希望这是有帮助的!

于 2013-05-01T08:15:31.640 回答