Elementos HTML

A classe Nette\Utils\Html é um auxiliar para gerar código HTML que previne a vulnerabilidade de Cross Site Scripting (XSS).

Funciona de tal forma que seus objetos representam elementos HTML, aos quais definimos parâmetros e os deixamos renderizar:

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

Instalação:

composer require nette/utils

Todos os exemplos pressupõem a criação de um alias:

use Nette\Utils\Html;

Criação de elementos HTML

Criamos um elemento usando o método Html::el():

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

Além do nome, você também pode especificar outros atributos na sintaxe HTML:

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

Ou passá-los como um array associativo no segundo parâmetro:

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

Alterando e retornando o nome do elemento:

$el->setName('img');
$el->getName(); // 'img'
$el->isEmpty(); // true, pois <img> é um elemento vazio

Atributos HTML

Podemos alterar e ler atributos HTML individuais de três maneiras; depende de você qual delas prefere. A primeira é através de propriedades:

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

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

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

A segunda maneira é chamando métodos, que, ao contrário da definição de propriedades, podemos encadear:

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

$el->alt(null); // remoção do atributo

E a terceira maneira é a mais verbosa:

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

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

$el->removeAttribute('alt');

Atributos podem ser definidos em massa usando addAttributes(array $attrs) e removidos usando removeAttributes(array $attrNames).

O valor de um atributo não precisa ser apenas uma string; valores booleanos também podem ser usados para atributos lógicos:

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

Um atributo também pode ser um array de valores, que serão impressos separados por espaços, o que é útil, por exemplo, para classes CSS:

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

Uma alternativa é um array associativo, onde os valores indicam se a chave deve ser impressa:

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

Estilos CSS podem ser escritos na forma de arrays associativos:

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

Até agora usamos propriedades, mas o mesmo pode ser escrito usando métodos:

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

Ou mesmo da maneira mais verbosa:

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

Um pequeno detalhe para concluir: o método href() pode facilitar a composição de parâmetros de consulta na URL:

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

Atributos de dados

Atributos de dados têm suporte especial. Como seus nomes contêm hífens, o acesso via propriedades e métodos não é tão elegante, por isso existe o método data():

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

Se o valor de um atributo de dados for um array, ele é automaticamente serializado para JSON:

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

Conteúdo do elemento

Definimos o conteúdo interno do elemento usando os métodos setHtml() ou setText(). Use o primeiro apenas se souber que está passando uma string HTML confiavelmente segura no 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>'

E, inversamente, obtemos o conteúdo interno usando os métodos getHtml() ou getText(). O segundo remove tags HTML da saída e converte entidades HTML em caracteres.

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

Nós filhos

O interior de um elemento também pode ser um array de nós filhos (children). Cada um deles pode ser uma string ou outro elemento Html. Nós os inserimos usando addHtml() ou addText():

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

Outra maneira de criar e inserir um novo nó Html:

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

É possível trabalhar com nós da mesma forma que com arrays. Ou seja, acessar nós individuais usando colchetes, contá-los usando count() e iterar sobre eles:

$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

Um novo nó pode ser inserido em uma posição específica usando insert(?int $index, $child, bool $replace = false). Se $replace = false, ele insere o elemento na posição $index e desloca os outros. Se $index = null, ele adiciona o elemento ao final.

// insere o elemento na primeira posição e desloca os outros
$el->insert(0, Html::el('span'));

Obtemos todos os nós usando o método getChildren() e os removemos usando o método removeChildren().

Criação de fragmento de documento

Se quisermos trabalhar com um array de nós e não nos importarmos com o elemento envolvente, podemos criar um chamado fragmento de documento passando null em vez do nome do elemento:

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

Uma maneira mais rápida de criar um fragmento é oferecida pelos métodos fromHtml() e fromText():

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

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

Geração de saída HTML

A maneira mais simples de imprimir um elemento HTML é usar echo ou converter o objeto para (string). Também é possível imprimir separadamente as tags de abertura ou fechamento e os 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"'

Uma característica importante é a proteção automática contra Cross Site Scripting (XSS). Todos os valores de atributos ou conteúdo inserido via setText() ou addText() são escapados de forma confiável:

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

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

Conversão HTML ↔ texto

Para converter HTML em texto, você pode usar o método estático htmlToText():

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

HtmlStringable

O objeto Nette\Utils\Html implementa a interface Nette\HtmlStringable, que, por exemplo, o Latte ou os formulários usam para distinguir objetos que possuem um método __toString() retornando código HTML. Assim, não ocorrerá dupla escapagem se, por exemplo, imprimirmos o objeto em um template usando {$el}.

versão: 4.0