Jak zdobyć innerHTML z DOMNode?

96

Jakiej funkcji używasz do uzyskania innerHTML danego DOMNode w implementacji PHP DOM? Czy ktoś może podać niezawodne rozwiązanie?

Oczywiście zewnętrznyHTML też się nada.

Dawid Ohia
źródło

Odpowiedzi:

152

Porównaj ten zaktualizowany wariant z uwagą użytkownika instrukcji obsługi PHP nr 89718 :

<?php 
function DOMinnerHTML(DOMNode $element) 
{ 
    $innerHTML = ""; 
    $children  = $element->childNodes;

    foreach ($children as $child) 
    { 
        $innerHTML .= $element->ownerDocument->saveHTML($child);
    }

    return $innerHTML; 
} 
?> 

Przykład:

<?php 
$dom= new DOMDocument(); 
$dom->preserveWhiteSpace = false;
$dom->formatOutput       = true;
$dom->load($html_string); 

$domTables = $dom->getElementsByTagName("table"); 

// Iterate over DOMNodeList (Implements Traversable)
foreach ($domTables as $table) 
{ 
    echo DOMinnerHTML($table); 
} 
?> 
Haim Evgi
źródło
Dzięki. To działa dobrze. Nie powinno $ dom-> preserveWhiteSpace = false; być przed załadowaniem dokumentu?
Dawid Ohia
@ JohnM2: Tak, powinno .
hakre
Dodatkowe uwagi: Od PHP 5.3.6 możesz oszczędzić tymczasowe DOMDocument. Również może chcieć zastąpić trimze związkiem ltrim(lub nawet usunąć go całkowicie), aby zachować trochę białe znaki jak łamanie wierszy.
hakre
Taka funkcja powinna zostać dodana do klasy DomDocument.
Nate
3
Musiałem zmienić deklarację funkcji, aby oczekiwać a DOMElementzamiast a, DOMNodeponieważ przekazywałem zwrot z DOMDocument::getElementById(). Na wypadek, gdyby ktoś podniósł się o coś.
miken32
25

Oto wersja w funkcjonalnym stylu programowania :

function innerHTML($node) {
    return implode(array_map([$node->ownerDocument,"saveHTML"], 
                             iterator_to_array($node->childNodes)));
}
trincot
źródło
13

Aby zwrócić htmlelement, możesz użyć C14N () :

$dom = new DOMDocument();
$dom->loadHtml($html);
$x = new DOMXpath($dom);
foreach($x->query('//table') as $table){
    echo $table->C14N();
}
CONvid19
źródło
2
C14N podejmie próbę konwersji kodu HTML na prawidłowy XML. Na przykład <br> zmieni się na <br> </br>
ajaybc
Jest to brudny sposób na zrzucenie kodu HTML elementu bez konieczności używania metody saveHTML, która wyświetli tagi html, head i body.
CONvid19
9

Uproszczona wersja odpowiedzi Haima Evgi:

<?php

function innerHTML(\DOMElement $element)
{
    $doc = $element->ownerDocument;

    $html = '';

    foreach ($element->childNodes as $node) {
        $html .= $doc->saveHTML($node);
    }

    return $html;
}

Przykładowe użycie:

<?php

$doc = new \DOMDocument();
$doc->loadHTML("<body><div id='foo'><p>This is <b>an <i>example</i></b> paragraph<br>\n\ncontaining newlines.</p><p>This is another paragraph.</p></div></body>");

print innerHTML($doc->getElementById('foo'));

/*
<p>This is <b>an <i>example</i></b> paragraph<br>

containing newlines.</p>
<p>This is another paragraph.</p>
*/

Nie ma potrzeby ustawiania preserveWhiteSpaceani formatOutput.

Alf Eaton
źródło
4

Oprócz ładnej wersji Trincota z array_mapi, implodeale tym razem z array_reduce:

return array_reduce(
   iterator_to_array($node->childNodes),
   function ($carry, \DOMNode $child) {
        return $carry.$child->ownerDocument->saveHTML($child);
   }
);

Nadal nie rozumiem, dlaczego nie ma reduce()metody, która akceptuje zarówno tablice, jak i iteratory.

grypa
źródło
3
function setnodevalue($doc, $node, $newvalue){
  while($node->childNodes->length> 0){
    $node->removeChild($node->firstChild);
  }
  $fragment= $doc->createDocumentFragment();
  $fragment->preserveWhiteSpace= false;
  if(!empty($newvalue)){
    $fragment->appendXML(trim($newvalue));
    $nod= $doc->importNode($fragment, true);
    $node->appendChild($nod);
  }
}
Chris
źródło
2

Oto inne podejście oparte na komentarzu Drupelli na php.net, które dobrze się sprawdziło w moim projekcie. Definiuje on innerHTML(), tworząc nowy DOMDocument, importując i dołączając do niego węzeł docelowy, zamiast jawnie iterować po węzłach potomnych.

InnerHTML

Zdefiniujmy tę funkcję pomocniczą:

function innerHTML( \DOMNode $n, $include_target_tag = true ) {
  $doc = new \DOMDocument();
  $doc->appendChild( $doc->importNode( $n, true ) );
  $html = trim( $doc->saveHTML() );
  if ( $include_target_tag ) {
      return $html;
  }
  return preg_replace( '@^<' . $n->nodeName .'[^>]*>|</'. $n->nodeName .'>$@', '', $html );
}

gdzie możemy dołączyć / wykluczyć zewnętrzny znacznik docelowy poprzez drugi argument wejściowy.

Przykład użycia

Tutaj wyodrębniamy wewnętrzny kod HTML dla znacznika docelowego podanego przez „pierwszy” atrybut id:

$html = '<div id="first"><h1>Hello</h1></div><div id="second"><p>World!</p></div>';
$doc  = new \DOMDocument();
$doc->loadHTML( $html );
$node = $doc->getElementById( 'first' );

if ( $node instanceof \DOMNode ) {

    echo innerHTML( $node, true );
    // Output: <div id="first"><h1>Hello</h1></div>    

    echo innerHTML( $node, false );
    // Output: <h1>Hello</h1>
}

Przykład na żywo:

http://sandbox.onlinephpfunctions.com/code/2714ea116aad9957c3c437d46134a1688e9133b8

birgire
źródło
1

Stara kwerenda, ale jest do tego wbudowana metoda. Po prostu przekaż węzeł docelowy do DomDocument->saveHtml().

Pełny przykład:

$html = '<div><p>ciao questa è una <b>prova</b>.</p></div>';
$dom = new DomDocument($html);
@$dom->loadHTML($html);
$xpath = new DOMXPath($dom);
$node = $xpath->query('.//div/*'); // with * you get inner html without surrounding div tag; without * you get inner html with surrounding div tag
$innerHtml = $dom->saveHtml($node);
var_dump($innerHtml);

Wynik: <p>ciao questa è una <b>prova</b>.</p>

Marco Marsala
źródło
Ostrzeżenie: DOMDocument :: saveHTML () oczekuje, że parametr 1 będzie DOMNode, obiekt podany
Ivan Gusev