Elementos HTML

La clase Nette\Utils\Html es un auxiliar para generar código HTML que ayuda a evitar las vulnerabilidades Cross-Site Scripting (XSS).

Funciona haciendo que sus objetos representen elementos HTML; usted fija sus parámetros y después los renderiza:

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

Puede rellenar el cuerpo de un elemento con texto y otros elementos mediante el método add(). El texto se escapa automáticamente y los elementos se insertan tal cual:

echo Html::el('div')->add(
	'Hello ',
	Html::el('b')->setText('world'),
);
// '<div>Hello <b>world</b></div>'

Instalación:

composer require nette/utils

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

use Nette\Utils\Html;

Crear un elemento HTML

Un elemento se crea con el método Html::el():

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

Además del nombre, puede indicar otros atributos con la sintaxis de HTML:

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

O pasarlos como array asociativo en el segundo parámetro:

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

Para cambiar y obtener el nombre del elemento:

$el->setName('img');
$el->getName(); // 'img'
$el->isEmpty(); // true, porque <img> es un elemento vacío

Atributos HTML

Los atributos HTML se pueden fijar y obtener de tres formas; usted decide cuál prefiere. La primera es mediante propiedades:

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

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

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

La segunda es llamando a métodos que, a diferencia de asignar propiedades, se pueden 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 es la más explícita:

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

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

$el->removeAttribute('alt');

Los atributos se pueden fijar en bloque con addAttributes(array $attrs) y eliminar con removeAttributes(array $attrNames).

El valor de un atributo no tiene por qué ser solo una cadena; en los atributos booleanos se pueden usar valores booleanos:

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

Un atributo puede ser también un array de valores, que se imprimen separados por espacios. Esto resulta útil, por ejemplo, para las clases CSS:

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

Una alternativa es un array asociativo, donde los valores indican si la clave debe incluirse:

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

Los estilos CSS se pueden escribir como arrays asociativos:

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

Hasta ahora hemos usado propiedades, pero lo mismo se puede lograr con 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 explícita:

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

Un último detalle: el método href() puede simplificar la composición de los parámetros de consulta de una URL:

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

Atributos data

Los atributos data tienen un soporte especial. Como sus nombres contienen guiones, acceder a ellos mediante propiedades y métodos no resulta tan elegante, así que existe un método específico, data():

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

Si el valor de un atributo data 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 interior del elemento se fija con los métodos setHtml() o setText(). Use el primero solo si está seguro de que el parámetro contiene una cadena HTML fiablemente segura.

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

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

A la inversa, el contenido interior se puede obtener con los métodos getHtml() o getText(). El segundo elimina las etiquetas HTML del contenido y convierte las entidades HTML de vuelta en caracteres.

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

Nodos hijos

El contenido interior de un elemento puede ser también un array de nodos hijos. Cada hijo puede ser una cadena u otro objeto Html. Se añaden con 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>

El método add() inserta varios hijos de una vez. Las cadenas se escapan igual que con addText(), los objetos Html se insertan tal cual y los valores null se saltan, lo que viene bien para el contenido condicional. Envuelva en Html::html() una cadena que sea HTML fiablemente seguro:

$el = Html::el('span')->add(
	'10 < 20',
	Html::el('br'),
	Html::html('hello<br>'),
	$showNote ? Html::el('small')->setText('note') : null,
);
// <span>10 &lt; 20<br>hello<br><small>note</small></span>

Otra forma de crear e insertar un nuevo nodo Html:

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

Con los nodos puede trabajar como si fueran elementos de un array. Es decir, acceder a cada nodo con corchetes, contarlos con count() e iterar 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 nodo nuevo en una posición concreta con 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ñade el elemento al final.

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

Todos los nodos se pueden obtener con el método getChildren() y eliminar con el método removeChildren().

Crear un fragmento de documento

Si quiere trabajar con un conjunto de nodos y no le importa el elemento envolvente, puede crear un fragmento de documento. Renderiza solo a sus hijos, sin etiqueta propia. El método fragment() lo crea y lo rellena con los hijos en una sola llamada, siguiendo las mismas reglas que add():

echo Html::fragment(
	Html::el('strong')->setText('hello'),
	'10 < 20',
	Html::el('br'),
);
// <strong>hello</strong>10 &lt; 20<br>

Un fragmento con contenido solo de texto o solo HTML se crea con los métodos text() y html():

echo Html::text('10 < 20');   // '10 &lt; 20'
echo Html::html('hello<br>'); // 'hello<br>'

Si necesita dar soporte a versiones anteriores a la 4.1.5, cree el fragmento pasando null en lugar del nombre del elemento y rellénelo con addHtml() y addText(). En lugar de text() y html(), esas versiones ofrecen los métodos fromText() y fromHtml(), que siguen funcionando pero están obsoletos:

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

echo Html::fromText('10 < 20');   // '10 &lt; 20'
echo Html::fromHtml('hello<br>'); // 'hello<br>'

Generar la salida HTML

La forma más sencilla de imprimir un elemento HTML es usar echo o convertir el objeto a (string). También puede imprimir por separado la etiqueta de apertura, la de cierre y los atributos:

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

echo $el;               // '<div class="header">hello</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"'

El método render(?int $indent = null) ofrece una impresión con formato. Si le pasa un nivel de sangría, la salida queda bien sangrada en varias líneas:

echo $el->render(0); // devuelve HTML indentado

Una característica importante es la protección automática contra Cross-Site Scripting (XSS). Todos los valores de atributo o el contenido insertados con setText(), addText(), add() o fragment() 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 usar 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. Latte y Forms usan esta interfaz, por ejemplo, para distinguir los objetos cuyo método __toString() devuelve código HTML. Esto evita el doble escapado si, por ejemplo, imprime el objeto en una plantilla con {$el}.

versión: 4.x