HTML elementy

Třída Nette\Utils\Html je pomocník pro generování HTML kódu, který nedovolí vznik zranitelnosti Cross Site Scripting (XSS).

Funguje tak, že jeho objekty představují HTML elementy, kterým nastavíme parametry a necháme je vykreslit:

$el = Html::el('img');  // vytvoří element <img>
$el->src = 'image.jpg'; // nastaví atribut src
echo $el;               // vypíše '<img src="image.jpg">'

Instalace:

composer require nette/utils

Všechny příklady předpokládají vytvořený alias:

use Nette\Utils\Html;

Vyvoření HTML elementu

Element vytvoříme metodou Html::el():

$el = Html::el('img'); // vytvoří element <img>

Kromě názvu můžete zadat i další atributy v HTML syntaxi:

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

Nebo je předat jako asociativní pole druhým parametrem:

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

Změna a vrácení názvu elementu:

$el->setName('img');
$el->getName(); // 'img'
$el->isEmpty(); // true, jelikož <img> je prázdný element

HTML atributy

Jednotlivé HTML atributy můžeme měnit a číst třemi způsoby, záleží na vás, který se vám bude líbit víc. První z nich je skrze property:

$el->src = 'image.jpg'; // nastaví atribut src

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

unset($el->src);  // zruší atribut
// nebo $el->src = null;

Druhou cestou je volání metod, které oproti nastavování properties můžeme za sebe řetězit:

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

$el->alt(null); // zrušení atributu

A třetí způsob je nejvíce upovídaný:

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

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

$el->removeAttribute('alt');

Hromadně lze atributy nastavit pomocí addAttributes(array $attrs) a odstranit pomocí removeAttributes(array $attrNames).

Hodnotou atributu nemusí být jen řetězec, lze používat i logické hodnoty pro logické atributy:

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

Atributem může být i pole hodnot, které se vypíší oddělené mezerami, což se hodí například pro CSS třídy:

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

Alternativou je asociativní pole, kde hodnoty říkají, zda má být klíč vypsán:

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

CSS styly lze zapisovat ve formě asociativních polí:

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

Nyní jsme používali property, ale totéž se dá zapsat pomocí metod:

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

Nebo i tím nejvíce upovídaným způsobem:

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

Ještě drobnost na závěr: metoda href() umí usnadnit skládání query parametrů v URL:

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

Data attributy

Speciální podporu mají datové atributy. Protože jejich názvy obsahují pomlčky, není přístup přes property a metody tak elegantní, proto existuje metoda data():

$el = Html::el('input');
$el->{'data-max-size'} = '500x300'; // není tolik elegantní
$el->data('max-size', '500x300'); // je elegatní
echo $el; // '<input data-max-size="500x300">'

Pokud je hodnotou datového attributu pole, automaticky se serializuje do JSONu:

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

Obsah elementu

Vnitřní obsah elementu nastavíme metodami setHtml() či setText(). První z nich použijte jen v případě, že víte, že v parametru předáváte spolehlivě bezpečný HTML řetězec.

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

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

A obráceně vnitřní obsah získáme metodami getHtml() či getText(). Druhá z nich odstraní z výstupu HTML značky a HTML entity převede na znaky.

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

Podřízené uzly

Vnitřek elementu může být také pole podřízených (children) uzlů. Každý z nich může být buď řetězec, nebo další Html element. Vkládáme je pomocí addHtml() či addText():

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

Další způsob pro vytvoření a vložení nového Html uzlu:

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

S uzly lze pracovat stejně, jako by se jednalo o pole. Tedy přistupovat k jednotlivým z nich pomocí hranatých závorek, spočítat je pomocí count() a iterovat nad nimi:

$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

Nový uzel lze na konkrétní místo vložit pomocí insert(?int $index, $child, bool $replace = false). Pokud je $replace = false, vloží prvek na pozici $index a ostatní posune. Pokud je $index = null, přidá prvek nakonec.

// vloží prvek na první pozici a ostatní posune
$el->insert(0, Html::el('span'));

Všechny uzly získáme metodou getChildren() a odstraníme je metodou removeChildren().

Vytvoření document fragment

Pokud chceme pracovat s polem uzlů a nezajímá nás obalovací element, můžeme vytvořit tzv. document fragment předáním null místo jména elementu:

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

Rychlejší způsob vytvoření fragmentu nabízí metody fromHtml() a fromText():

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

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

Generování HTML výstupu

Nejjednodušším způsobem, jak vypsat HTML element, je použít echo nebo objekt přetypovat na (string). Lze také samostatně vypsat otevírací nebo uzavírací značky a atributy:

$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"'

Důležitým rysem je automatická ochrana proti Cross Site Scriptingu (XSS). Všechny hodnoty atributů nebo obsah vložený přes setText() či addText() se spolehlivě escapuje:

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

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

Výstup je možné přepnout do XHTML nastavením statické proměnné Html::$xhtml na true:

$el = Html::el('input')->disabled(true);
echo $el; // '<input disabled>'
Html::$xhtml = true;
echo $el; // '<input disabled="disabled" />'

Konverze HTML ↔ text

Pro převod HTML do textu můžete využít statickou metodu htmlToText():

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

HtmlStringable

Objekt Nette\Utils\Html implementuje rozhraní Nette\HtmlStringable, kterým například Latte nebo formuláře rozlišují objekty, které mají metodu __toString() vracející HTML kód. Takže nedojde k dvojímu escapování, pokud třeba objekt vypíšeme v šabloně pomocí {$el}.

V nette/utils před verzí 3.2 se rozhraní jmenovalo Nette\Utils\IHtmlString.

verze: 4.0 3.x 2.x