HTML елементи

Класът [api:Nette\Utils\Html] е помощник за генериране на HTML код, който предотвратява уязвимостта Cross Site Scripting (XSS).

Работи така, че неговите обекти представляват HTML елементи, на които задаваме параметри и ги рендираме:

$el = Html::el('img');  // създава елемент <img>
$el->src = 'image.jpg'; // задава атрибут src
echo $el;               // извежда '<img src="image.jpg">'

Инсталация:

composer require nette/utils

Всички примери предполагат създаден псевдоним (alias):

use Nette\Utils\Html;

Създаване на HTML елемент

Елементът се създава с метода Html::el():

$el = Html::el('img'); // създава елемент <img>

Освен името, можете да зададете и други атрибути в HTML синтаксис:

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

Или да ги предадете като асоциативен масив във втория параметър:

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

Промяна и връщане на името на елемента:

$el->setName('img');
$el->getName(); // 'img'
$el->isEmpty(); // true, тъй като <img> е празен елемент

HTML атрибути

Можем да променяме и четем отделните HTML атрибути по три начина, зависи от вас кой ще ви хареса повече. Първият е чрез свойства (properties):

$el->src = 'image.jpg'; // задава атрибут src

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

unset($el->src);  // премахва атрибута
// или $el->src = null;

Вторият начин е извикването на методи, които за разлика от задаването на свойства, можем да свързваме верижно:

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

$el->alt(null); // премахване на атрибута

А третият начин е най-многословен:

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

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

$el->removeAttribute('alt');

Атрибутите могат да се задават групово с addAttributes(array $attrs) и да се премахват с removeAttributes(array $attrNames).

Стойността на атрибута не трябва да бъде само низ, могат да се използват и булеви стойности за булеви атрибути:

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

Атрибутът може да бъде и масив от стойности, които се извеждат разделени с интервали, което е полезно например за CSS класове:

$el = Html::el('input');
$el->class[] = 'active';
$el->class[] = null; // null се игнорира
$el->class[] = 'top';
echo $el; // '<input class="active top">'

Алтернатива е асоциативен масив, където стойностите показват дали ключът трябва да бъде изведен:

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

CSS стиловете могат да се записват под формата на асоциативни масиви:

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

Сега използвахме свойства, но същото може да се запише и с помощта на методи:

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

Или дори по най-многословния начин:

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

Още една дреболия накрая: методът href() може да улесни съставянето на query параметри в URL:

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

Data атрибути

Data атрибутите имат специална поддръжка. Тъй като имената им съдържат тирета, достъпът чрез свойства и методи не е толкова елегантен, затова съществува методът data():

$el = Html::el('input');
$el->{'data-max-size'} = '500x300'; // не е толкова елегантно
$el->data('max-size', '500x300'); // е елегантно
echo $el; // '<input data-max-size="500x300">'

Ако стойността на data атрибута е масив, той автоматично се сериализира в JSON:

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

Съдържание на елемента

Вътрешното съдържание на елемента се задава с методите setHtml() или setText(). Използвайте първия само ако знаете, че предавате надеждно безопасен HTML низ в параметъра.

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

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

И обратно, вътрешното съдържание се получава с методите getHtml() или getText(). Вторият премахва HTML таговете от изхода и преобразува HTML същностите (entities) в символи.

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

Дъщерни възли

Вътрешността на елемента може да бъде и масив от дъщерни възли (children). Всеки от тях може да бъде или низ, или друг Html елемент. Вмъкваме ги с помощта на addHtml() или addText():

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

Друг начин за създаване и вмъкване на нов Html възел:

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

С възлите може да се работи по същия начин, както с масив. Тоест, достъп до отделните възли с квадратни скоби, преброяване с count() и итериране върху тях:

$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

Нов възел може да бъде вмъкнат на конкретно място с помощта на insert(?int $index, $child, bool $replace = false). Ако $replace = false, вмъква елемента на позиция $index и измества останалите. Ако $index = null, добавя елемента в края.

// вмъква елемента на първа позиция и измества останалите
$el->insert(0, Html::el('span'));

Всички възли се получават с метода getChildren() и се премахват с метода removeChildren().

Създаване на document fragment

Ако искаме да работим с масив от възли и не ни интересува обвиващият елемент, можем да създадем т.нар. document fragment, като предадем null вместо име на елемент:

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

По-бърз начин за създаване на фрагмент предлагат методите fromHtml() и fromText():

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

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

Генериране на HTML изход

Най-лесният начин да изведете HTML елемент е да използвате echo или да преобразувате обекта към (string). Можете също така да изведете отделно отварящия или затварящия таг и атрибутите:

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

Важна характеристика е автоматичната защита срещу Cross Site Scripting (XSS). Всички стойности на атрибути или съдържание, вмъкнати чрез setText() или addText(), се екранират надеждно:

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

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

Конвертиране на HTML ↔ текст

За преобразуване на HTML в текст можете да използвате статичния метод htmlToText():

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

HtmlStringable

Обектът Nette\Utils\Html имплементира интерфейса Nette\HtmlStringable, чрез който например Latte или формите разграничават обекти, които имат метод __toString(), връщащ HTML код. Така че няма да се получи двойно екраниране, ако например изведем обекта в шаблон с помощта на {$el}.

версия: 4.0