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&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 < 20</span>'
И обратно, вътрешното съдържание се получава с методите getHtml()
или getText()
. Вторият премахва HTML таговете от изхода и преобразува
HTML същностите (entities) в символи.
echo $el->getHtml(); // '10 < 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 < 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 < 20<br>
По-бърз начин за създаване на фрагмент предлагат методите
fromHtml()
и fromText()
:
$el = Html::fromHtml('hello<br>');
echo $el; // 'hello<br>'
$el = Html::fromText('10 < 20');
echo $el; // '10 < 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()'><script>bad()</script></div>
Конвертиране на HTML ↔ текст
За преобразуване на HTML в текст можете да използвате статичния метод
htmlToText()
:
echo Html::htmlToText('<span>One & Two</span>'); // 'One & Two'
HtmlStringable
Обектът Nette\Utils\Html
имплементира интерфейса Nette\HtmlStringable
,
чрез който например Latte или формите разграничават обекти, които имат
метод __toString()
, връщащ HTML код. Така че няма да се получи двойно
екраниране, ако например изведем обекта в шаблон с помощта на
{$el}
.