Elementos HTML

La clase Nette\Utils\Html es una ayuda para generar código HTML que previene la vulnerabilidad Cross Site Scripting (XSS).

Funciona de tal forma que sus objetos representan elementos HTML, establecemos sus parámetros y dejamos que se rendericen:

$el = Html::el('img');  // crea el elemento <img>.
$el->src = 'image.jpg'; // establece el atributo src
echo $el;               // imprime '<img src="imagen.jpg">'

Instalación:

composer require nette/utils

Todos los ejemplos asumen que el siguiente alias de clase está definido:

use Nette\Utils\Html;

Creación de un elemento HTML

El elemento se crea utilizando el método Html::el():

$el = Html::el('img'); // crea el elemento <img>

Además del nombre, puede introducir otros atributos en la sintaxis HTML:

$el = Html::el('input type=text class="red important"');

O pasarlos como un array asociativo al segundo parámetro:

$el = Html::el('input', [
	'type' => 'text',
	'class' => 'important',
]);

Para cambiar y devolver el nombre de un elemento:

$el->setName('img');
$el->getName(); // 'img'
$el->isEmpty(); // true, as <img> is void element

Atributos HTML

Puedes establecer y obtener atributos HTML individuales de tres maneras, depende de quién te guste más. La primera es a través de las propiedades:

$el->src = 'image.jpg'; // establece el atributo src

echo $el->src; // 'image.jpg'

unset($el->src);  // elimina el atributo
// or $el->src = null;

La segunda forma es llamar a métodos que, a diferencia de establecer propiedades, podemos encadenar:

$el = Html::el('img')->src('image.jpg')->alt('photo');
// <img src="image.jpg" alt="photo">

$el->alt(null); // elimina el atributo

Y la tercera forma es la más locuaz:

$el = Html::el('img')
	->setAttribute('src', 'image.jpg')
	->setAttribute('alt', 'photo');

echo $el->getAttribute('src'); // 'image.jpg'

$el->removeAttribute('alt');

En bloque, los atributos pueden fijarse con addAttributes(array $attrs) y borrarse con removeAttributes(array $attrNames).

El valor de un atributo no tiene por qué ser sólo una cadena, también pueden utilizarse valores lógicos para atributos lógicos:

$checkbox = Html::el('input')->type('checkbox');
$checkbox->checked = true;  // <input type="checkbox" checked>
$checkbox->checked = false; // <input type="checkbox">

Un atributo también puede ser una matriz de tokens, que se enumeran separados por espacios, lo que es adecuado para las clases CSS, por ejemplo:

$el = Html::el('input');
$el->class[] = 'active';
$el->class[] = null; // se ignora null
$el->class[] = 'top';
echo $el; // '<input class="active top">'

Una alternativa es un array asociativo, donde los valores dicen si la clave debe ser listada:

$el = Html::el('input');
$el->class['active'] = true;
$el->class['top'] = false;
echo $el; // '<input class="active">'

Los estilos CSS pueden escribirse en forma de matrices asociativas:

$el = Html::el('input');
$el->style['color'] = 'green';
$el->style['display'] = 'block';
echo $el; // '<input style="color: green; display: block">'

Ahora hemos utilizado propiedades, pero se puede hacer lo mismo utilizando los métodos:

$el = Html::el('input');
$el->style('color', 'green');
$el->style('display', 'block');
echo $el; // '<input style="color: green; display: block">'

O incluso de la forma más locuaz:

$el = Html::el('input');
$el->appendAttribute('style', 'color', 'green');
$el->appendAttribute('style', 'display', 'block');
echo $el; // '<input style="color: green; display: block">'

Una última cosa: el método href() puede facilitar la composición de los parámetros de consulta en una URL:

echo Html::el('a')->href('index.php', [
	'id' => 10,
	'lang' => 'en',
]);
// '<a href="index.php?id=10&amp;lang=en"></a>'

Atributos de datos

Los atributos de datos tienen un soporte especial. Debido a que sus nombres contienen guiones, el acceso a través de propiedades y métodos no es tan elegante, por lo que existe un método data():

$el = Html::el('input');
$el->{'data-max-size'} = '500x300'; // not so elegant
$el->data('max-size', '500x300'); // is elegant
echo $el; // '<input data-max-size="500x300">'

Si el valor del atributo de datos es un array, se serializa automáticamente a JSON:

$el = Html::el('input');
$el->data('items', [1,2,3]);
echo $el; // '<input data-items="[1,2,3]">'

Contenido del elemento

El contenido interno del elemento se establece mediante los métodos setHtml() o setText(). Utilice el primero sólo si sabe que está pasando de forma fiable una cadena HTML segura en el parámetro.

echo Html::el('span')->setHtml('hello<br>');
// '<span>hello<br></span>'

echo Html::el('span')->setText('10 < 20');
// '<span>10 &lt; 20</span>'

Por el contrario, el contenido interno se obtiene mediante los métodos getHtml() o getText(). El segundo elimina las etiquetas de la salida HTML y convierte las entidades HTML en caracteres.

echo $el->getHtml(); // '10 &lt; 20'
echo $el->getText(); // '10 < 20'

Nodos hijos

El contenido interno de un elemento también puede ser un array de hijos. Cada uno de ellos puede ser una cadena u otro elemento de Html. Se insertan utilizando addHtml() o addText():

$el = Html::el('span')
	->addHtml('hello<br>')
	->addText('10 < 20')
	->addHtml( Html::el('br') );
// <span>hello<br>10 &lt; 20<br></span>

Otra forma de crear e insertar un nuevo nodo Html:

$el = Html::el('ul')
	->create('li', ['class' => 'first'])
		->setText('hello');
// <ul><li class="first">hello</li></ul>

Puedes trabajar con nodos como si fueran elementos de un array. Así que accede a los individuales usando corchetes, cuéntalos con count() e itera sobre ellos:

$el = Html::el('div');
$el[] = '<b>hello</b>';
$el[] = Html::el('span');
echo $el[1]; // '<span></span>'

foreach ($el as $child) { /* ... */ }

echo count($el); // 2

Se puede insertar un nuevo nodo en una posición específica utilizando insert(?int $index, $child, bool $replace = false). Si $replace = false, inserta el elemento en la posición $index y desplaza los demás. Si $index = null, añadirá un elemento al final.

// inserta el elemento en la primera posición y avanza los demás
$el->insert(0, Html::el('span'));

Todos los nodos son devueltos por el método getChildren() y eliminados por el método removeChildren().

Creación de un fragmento de documento

Si desea trabajar con una matriz de nodos y no está interesado en el elemento envolvente, puede crear un llamado fragmento de documento pasando null en lugar del nombre del elemento:

$el = Html::el(null)
	->addHtml('hello<br>')
	->addText('10 < 20')
	->addHtml( Html::el('br') );
// hello<br>10 &lt; 20<br>

Los métodos fromHtml() y fromText() ofrecen una forma más rápida de crear un fragmento:

$el = Html::fromHtml('hello<br>');
echo $el; // 'hello<br>'

$el = Html::fromText('10 < 20');
echo $el; // '10 &lt; 20'

Generar salida HTML

La forma más sencilla de generar un elemento HTML es utilizar echo o lanzar un objeto a (string). También puede imprimir etiquetas de apertura o cierre y atributos por separado:

$el = Html::el('div class=header')->setText('hello');

echo $el;               // '<div class="header"></div>'
$s = (string) $el;      // '<div class="header">hello</div>'
$s = $el->toHtml();     // '<div class="header">hello</div>'
$s = $el->toText();     // 'hello'
echo $el->startTag();   // '<div class="header">'
echo $el->endTag();     // '</div>'
echo $el->attributes(); // 'class="header"'

Una característica importante es la protección automática contra Cross Site Scripting (XSS). Todos los valores de atributos o contenidos insertados mediante setText() o addText() se escapan de forma fiable:

echo Html::el('div')
	->title('" onmouseover="bad()')
	->setText('<script>bad()</script>');

// <div title='" onmouseover="bad()'>&lt;script&gt;bad()&lt;/script&gt;</div>

Conversión HTML ↔ Texto

Puede utilizar el método estático htmlToText() para convertir HTML en texto:

echo Html::htmlToText('<span>One &amp; Two</span>'); // 'One & Two'

HtmlStringable

El objeto Nette\Utils\Html implementa la interfaz Nette\HtmlStringable, que, por ejemplo, Latte o forms utilizan para distinguir los objetos que tienen un método __toString() que devuelve código HTML. Así, el doble escape no se produce si, por ejemplo, imprimimos el objeto en la plantilla utilizando {$el}.