[Go to site: main page, start]

Elementy HTML

Klasa Nette\Utils\Html to pomocnik do generowania kodu HTML, który pomaga zapobiegać podatnościom Cross-Site Scripting (XSS).

Działa tak, że jej obiekty reprezentują elementy HTML; ustawiasz ich parametry, a następnie je renderujesz:

$el = Html::el('img');  // tworzy element <img>
$el->src = 'image.jpg'; // ustawia atrybut src
echo $el;               // wypisze '<img src="image.jpg">'

Ciało elementu możesz wypełnić tekstem i innymi elementami metodą add(). Tekst jest automatycznie escapowany, elementy wstawiane są takie, jakie są:

echo Html::el('div')->add(
	'Cześć ',
	Html::el('b')->setText('świecie'),
);
// '<div>Cześć <b>świecie</b></div>'

Instalacja:

composer require nette/utils

Wszystkie przykłady zakładają, że zdefiniowany jest następujący alias klasy:

use Nette\Utils\Html;

Tworzenie elementu HTML

Element tworzy się metodą Html::el():

$el = Html::el('img'); // tworzy element <img>

Oprócz nazwy możesz podać także inne atrybuty, używając składni HTML:

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

Albo przekazać je jako tablicę asocjacyjną w drugim parametrze:

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

Aby zmienić i pobrać nazwę elementu:

$el->setName('img');
$el->getName(); // 'img'
$el->isEmpty(); // true, bo <img> jest elementem pustym

Atrybuty HTML

Poszczególne atrybuty HTML można ustawiać i pobierać na trzy sposoby; to od Ciebie zależy, który wolisz. Pierwszy to właściwości:

$el->src = 'image.jpg'; // ustawia atrybut src

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

unset($el->src);  // usuwa atrybut
// albo $el->src = null;

Drugi sposób to wywoływanie metod, które w odróżnieniu od ustawiania właściwości można łączyć w łańcuch:

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

$el->alt(null); // usuwa atrybut

A trzeci sposób jest najbardziej rozwlekły:

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

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

$el->removeAttribute('alt');

Atrybuty można ustawiać hurtowo metodą addAttributes(array $attrs) i usuwać metodą removeAttributes(array $attrNames).

Wartością atrybutu nie musi być tylko string; dla atrybutów logicznych można używać wartości logicznych:

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

Atrybutem może być też tablica wartości, które wypisywane są oddzielone spacjami. Przydaje się to na przykład przy klasach CSS:

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

Alternatywą jest tablica asocjacyjna, w której wartości wskazują, czy klucz ma zostać uwzględniony:

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

Style CSS można zapisywać jako tablice asocjacyjne:

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

Do tej pory używaliśmy właściwości, ale to samo można osiągnąć metodami:

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

Albo nawet w najbardziej rozwlekły sposób:

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

Na koniec drobiazg: metoda href() potrafi uprościć składanie parametrów zapytania w URL:

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

Atrybuty data

Atrybuty data mają specjalne wsparcie. Ponieważ ich nazwy zawierają myślniki, sięganie po nie przez właściwości i metody nie jest zbyt eleganckie, dlatego istnieje dedykowana metoda data():

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

Jeśli wartością atrybutu data jest tablica, zostanie automatycznie zserializowana do JSON:

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

Treść elementu

Wewnętrzną treść elementu ustawia się metodami setHtml() albo setText(). Pierwszej używaj tylko wtedy, gdy masz pewność, że parametr zawiera niezawodnie bezpieczny string HTML.

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

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

Odwrotnie, wewnętrzną treść można pobrać metodami getHtml() albo getText(). Ta druga usuwa z treści tagi HTML i zamienia encje HTML z powrotem na znaki.

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

Węzły potomne

Wewnętrzną treścią elementu może być również tablica węzłów potomnych. Każdy potomek może być stringiem albo innym obiektem Html. Dodaje się je metodami addHtml() albo addText():

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

Metoda add() wstawia kilku potomków naraz. Stringi są escapowane tak samo jak przy addText(), obiekty Html wstawiane są takie, jakie są, a wartości null pomijane, co przydaje się przy treści warunkowej. String, który jest niezawodnie bezpiecznym HTML, opakuj w Html::html():

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

Inny sposób utworzenia i wstawienia nowego węzła Html:

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

Z węzłami możesz pracować tak, jakby były elementami tablicy. Czyli sięgać po poszczególne węzły przez nawiasy kwadratowe, liczyć je przez count() i po nich iterować:

$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

Nowy węzeł można wstawić na konkretnej pozycji metodą insert(?int $index, $child, bool $replace = false). Jeśli $replace = false, wstawia element na pozycji $index i przesuwa pozostałe. Jeśli $index = null, dołącza element na końcu.

// wstawia element na pierwszej pozycji i przesuwa pozostałe
$el->insert(0, Html::el('span'));

Wszystkie węzły można pobrać metodą getChildren() i usunąć metodą removeChildren().

Tworzenie fragmentu dokumentu

Jeśli chcesz pracować ze zbiorem węzłów i nie zależy Ci na elemencie otaczającym, możesz utworzyć fragment dokumentu. Renderuje on tylko swoich potomków, bez własnego tagu. Metoda fragment() tworzy go i wypełnia potomkami w jednym wywołaniu, według tych samych reguł co add():

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

Fragment z treścią wyłącznie tekstową albo wyłącznie HTML tworzą metody text() i html():

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

Jeśli musisz wspierać wersje starsze niż 4.1.5, utwórz fragment, przekazując null zamiast nazwy elementu, i wypełnij go metodami addHtml() i addText(). Zamiast text() i html() wersje te oferują metody fromText() i fromHtml(), które nadal działają, ale są przestarzałe:

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

Generowanie wyniku HTML

Najprostszym sposobem wypisania elementu HTML jest użycie echo albo rzutowanie obiektu na (string). Możesz też wypisać osobno tag otwierający, zamykający i atrybuty:

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

Metoda render(?int $indent = null) oferuje ładne formatowanie. Jeśli podasz poziom wcięcia, wynik zostanie ładnie wcięty i rozbity na kilka wierszy:

echo $el->render(0); // zwraca wcięty HTML

Ważną cechą jest automatyczna ochrona przed Cross-Site Scripting (XSS). Wszystkie wartości atrybutów oraz treść wstawiana przez setText(), addText(), add() albo fragment() są niezawodnie escapowane:

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

// <div title='" 

Konwersja HTML ↔ tekst

Do konwersji HTML na tekst możesz użyć statycznej metody htmlToText():

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

HtmlStringable

Obiekt Nette\Utils\Html implementuje interfejs Nette\HtmlStringable. Latte i Forms używają tego interfejsu na przykład do rozróżniania obiektów, które mają metodę __toString() zwracającą kod HTML. Zapobiega to podwójnemu escapowaniu, gdy na przykład wypiszesz obiekt w szablonie przez {$el}.

wersja: 4.x