Elementos HTML

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

Funciona de tal manera que sus objetos representan elementos HTML, a los que establecemos 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="image.jpg">'

Instalación:

composer require nette/utils

Todos los ejemplos asumen que se ha creado un alias:

use Nette\Utils\Html;

Creación de elementos HTML

Creamos un elemento con el método Html::el():

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

Además del nombre, también puedes especificar otros atributos en sintaxis HTML:

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

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

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

Cambiar y devolver el nombre del elemento:

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

Atributos HTML

Podemos cambiar y leer atributos HTML individuales de tres maneras, depende de ti cuál te guste más. La primera es a través de 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 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); // Eliminación del atributo

Y la tercera forma es la más verbosa:

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

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

$el->removeAttribute('alt');

Los atributos se pueden establecer en masa usando addAttributes(array $attrs) y eliminar usando removeAttributes(array $attrNames).

El valor de un atributo no tiene que ser solo una cadena, también se pueden usar valores booleanos para atributos booleanos:

$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 un array de valores, que se imprimirán separados por espacios, lo cual es útil, por ejemplo, para 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 imprimirse:

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

Los estilos CSS se pueden escribir en forma de arrays asociativos:

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

Ahora hemos usado propiedades, pero lo mismo se puede escribir usando 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 manera más verbosa:

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

Un pequeño detalle para terminar: el método href() puede facilitar la composición de 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 soporte especial. Debido a que sus nombres contienen guiones, el acceso a través de propiedades y métodos no es tan elegante, por eso existe el método data():

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

Si el valor de un 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

Establecemos el contenido interno del elemento con los métodos setHtml() o setText(). Use el primero solo si sabe que está pasando una cadena HTML confiablemente 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>'

Y viceversa, obtenemos el contenido interno con los métodos getHtml() o getText(). El segundo elimina las etiquetas HTML de la salida y convierte las entidades HTML en caracteres.

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

Nodos hijos

El interior de un elemento también puede ser un array de nodos hijos (children). Cada uno de ellos puede ser una cadena u otro elemento Html. Los insertamos usando 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:

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

Se puede trabajar con los nodos como si fueran un array. Es decir, acceder a ellos individualmente usando corchetes, contarlos usando 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 nuevo nodo en una ubicación específica usando 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'));

Obtenemos todos los nodos con el método getChildren() y los eliminamos con el método removeChildren().

Creación de fragmento de documento

Si queremos trabajar con un array de nodos y no nos interesa el elemento contenedor, podemos 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>

Una forma más rápida de crear un fragmento la ofrecen los métodos fromHtml() y fromText():

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

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

Generación de salida HTML

La forma más sencilla de imprimir un elemento HTML es usar echo o convertir (cast) el objeto a (string). También es posible imprimir por separado las etiquetas de apertura o 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"'

Una característica importante es la protección automática contra Cross Site Scripting (XSS). Todos los valores de atributos o contenido insertado a través de 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

Para convertir HTML a texto, puede utilizar el método estático htmlToText():

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

HtmlStringable

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

versión: 4.0