¿Cómo obtener innerHTML de DOMNode?

96

¿Qué función usa para obtener innerHTML de un DOMNode dado en la implementación PHP DOM? ¿Alguien puede dar una solución confiable?

Por supuesto, el HTML externo también lo hará.

Dawid Ohia
fuente

Respuestas:

152

Compare esta variante actualizada con la nota de usuario del manual PHP # 89718 :

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

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

    return $innerHTML; 
} 
?> 

Ejemplo:

<?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
fuente
Gracias. Funciona bien. No debería $ dom-> preserveWhiteSpace = false; estar antes de la carga del documento?
Dawid Ohia
@ JohnM2: Sí, debería .
hakre
Notas adicionales: Desde PHP 5.3.6 puede ahorrar el archivo temporal DOMDocument. También es posible que desee reemplazar el trimcon un ltrim(o incluso eliminarlo por completo) para preservar un poco del espacio en blanco como saltos de línea.
hakre
Una función como esta debería agregarse a la clase DomDocument.
Nate
3
Tuve que cambiar la declaración de la función para esperar un en DOMElementlugar de un DOMNodecomo estaba pasando el retorno DOMDocument::getElementById(). Por si acaso tropieza con alguien más.
miken32
25

Aquí hay una versión en un estilo de programación funcional :

function innerHTML($node) {
    return implode(array_map([$node->ownerDocument,"saveHTML"], 
                             iterator_to_array($node->childNodes)));
}
trincot
fuente
13

Para devolver el htmlde un elemento, puede usar C14N () :

$dom = new DOMDocument();
$dom->loadHtml($html);
$x = new DOMXpath($dom);
foreach($x->query('//table') as $table){
    echo $table->C14N();
}
CONvid19
fuente
2
C14N intentará convertir el HTML a un XML válido. Por ejemplo, <br> se convertirá en <br> </br>
ajaybc
Es una forma sucia de volcar el HTML del elemento, sin tener que usar saveHTML que generará etiquetas html, head y body.
CONvid19
9

Una versión simplificada de la respuesta de Haim Evgi:

<?php

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

    $html = '';

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

    return $html;
}

Uso de ejemplo:

<?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>
*/

No es necesario configurar preserveWhiteSpaceo formatOutput.

Alf Eaton
fuente
4

Además de la bonita versión de trincot con array_mapy implodepero esta vez con array_reduce:

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

Todavía no entiendo por qué no hay un reduce()método que acepte matrices e iteradores por igual.

gripe
fuente
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
fuente
2

Aquí hay otro enfoque basado en este comentario de Drupella en php.net, que funcionó bien para mi proyecto. Define el innerHTML()al crear un nuevo DOMDocument, importar y agregarle el nodo de destino, en lugar de iterar explícitamente sobre los nodos secundarios.

InnerHTML

Definamos esta función auxiliar:

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 );
}

donde podemos incluir / excluir la etiqueta de destino externa a través del segundo argumento de entrada.

Ejemplo de uso

Aquí extraemos el HTML interno para una etiqueta de destino dada por el atributo de identificación "primer":

$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>
}

Ejemplo en vivo:

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

Birgire
fuente
1

Consulta antigua, pero hay un método integrado para hacerlo. Simplemente pase el nodo de destino a DomDocument->saveHtml().

Ejemplo completo:

$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);

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

Marco Marsala
fuente
Advertencia: DOMDocument :: saveHTML () espera que el parámetro 1 sea DOMNode, objeto dado
Ivan Gusev