HTML DOM:如何在不丢失孩子的情况下获取元素?

时间:2011-03-14 18:19:05

标签: php dom

我正在尝试在HTML字符串中对文本执行preg_replace。我想避免替换标签中的文本,因此我将字符串作为DOM元素加载并抓取每个节点中的文本。例如,我有这个清单:

<ul>
<li><a href="?p=oconnorinv&i=1">Boxes 1-3</a>: 1925 - 1928 <em>(A-Ma)</em></li>
<li><a href="?p=oconnorinv&i=2">Boxes 4-6</a>: 1928 <em>(Mb-Z)</em> - 1930 <em>(A-Wi)</em></li>
<li><a href="?p=oconnorinv&i=3">Boxes 7-9</a>: 1930 <em>(Wo-Z)</em>- 1932 <em>(A-Fl)</em></li>
</ul>

我希望能够突出显示字符“1”或字母“i”,而不会打扰链接或列表项标记。所以我抓住每个列表项并获取其值来执行替换:

$invfile = [string of the unordered list above]
$invcontents = new DOMDocument;
$invcontents->loadHTML($invfile);
$inv_listitems = $invcontents->getElementsByTagName('li');
    foreach ($inv_listitems as $f) {
            $f->nodeValue = preg_replace($to_highlight, "<span class=\"highlight\">$0</span>", $f->nodeValue);
        }
    echo html_entity_decode($invcontents->saveHTML());

问题是,当我获取节点值时,列表项中的子节点将丢失。如果按原样打印出原始字符串,则&lt; a&gt;,&lt; em&gt;等标签都在那里。但是当我运行脚本时,它打印出来没有链接或任何格式标签。例如,如果我的$ to_replace是字符串“Boxes”,则列表变为:

<ul>
<li><span class="highlight">Boxes</span> 1-3: 1925 - 1928 (A-Ma)</li>
<li><span class="highlight">Boxes</span> 4-6: 1928 (Mb-Z) - 1930 (A-Wi)</li>
<li><span class="highlight">Boxes</span> 7-9: 1930 (Wo-Z)- 1932 (A-Fl)</li>
</ul>

如何在不丢失内部标签的情况下获取文本?

3 个答案:

答案 0 :(得分:0)

这里的问题是你在整个

  • 元素上运行。 Box是锚标记的nodeValue的一部分。

    如果上面的结构总是相同的,你可以做类似

    的事情

    $ new_html = preg_replace(“##”,“”,$ f-&gt; item(0) - &gt; nodeValue);

    实际上,最好的方法是取消设置锚点的节点值并创建一个全新的元素并附加它。

    (考虑这个伪代码)

    $inv_listitems = $invcontents->getElementsByTagName('li');
    foreach ($inv_listitems as $f) {
            $span = $invcontents->createElement("span");
            $span->setAttribute("class", "highlight");
            $span->nodeValue = $f->item(0)->nodeValue;
            $f->appendChild($span);
        }
    echo $invcontents->saveHTML();
    

    你必须在那里进行一些匹配,以及取消设置$ f的nodeValue,但希望这会让它更加清晰。

    另外,不要直接在nodeValue中设置HTML,因为它会对你设置的所有html运行htmlentities()。这就是我在上面创建一个新元素的原因。如果您必须在nodeValue中设置HTML,则应创建DocumentFragment Object

  • 答案 1 :(得分:0)

    你最好只在文本节点上操作:

    $x  = new DOMXPath(invcontents);
    foreach($x->query('//li/text()' as $textnode){
        //replace text node with list of plain text nodes & your highlighting span.
    }
    

    答案 2 :(得分:0)

    我总是使用xpath进行这种操作。它会给你更多的灵活性。 此示例处理

    <mainlevel>
      <toplevel>
        <detaillevel key=...>
          <xmlvalue1></xmlvalue1>
          <xmlvalue1></xmlvalue2>
    
          <sublevel key=...>
            <xmlvalue1></xmlsubvalue1>
            <xmlvalue1></xmlsubvalue2>
          </sublevel>
    
        </detaillevel>
      </toplevel>
    </mainlevel>
    

    解析这个:

    $xpath = new DOMXPath($xmlDoc);
    $mainNodes = $xpath->query("/mainlevel/toplevel/detaillevel");
    
    foreach( $mainNodes as $subNode ) { 
        $parameter1=$subNode->getAttribute('key');
        $parameter2=$subNode->getElementsByTagName("xmlvalue1")->item(0)->nodeValue;
        $parameter3=$subNode->getElementsByTagName("xmlvalue2")->item(0)->nodeValue;
    
        foreach ($subNode->getElementsByTagName("sublevel") as $detailNode) {
            $parameter1=$detailNode->getAttribute('key');
            $parameter2=$detailNode->getAttribute('xmlsubvalue1');
            $parameter2=$detailNode->getAttribute('xmlsubvalue2');
    
            }
        }
    
    相关问题