Элементы HTML
Класс 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
Во всех примерах предполагается, что псевдоним уже создан:
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, и вы сами решаете, какой из них вам больше нравится. Первый – через собственность:
$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 se ignoruje
$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()
может облегчить составление
параметров запроса в URL:
echo Html::el('a')->href('index.php', [
'id' => 10,
'lang' => 'en',
]);
// '<a href="index.php?id=10&lang=en"></a>'
Атрибуты данных
Атрибуты данных имеют специальную поддержку. Поскольку их имена
содержат дефисы, доступ через свойства и методы не так элегантен,
поэтому существует метод 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">'
Если значением атрибута данных является массив, он автоматически сериализуется в 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-сущности в символы.
echo $el->getHtml(); // '10 < 20'
echo $el->getText(); // '10 < 20'
Дочерние узлы
Внутри элемента также может быть массив дочерних узлов. Каждый из них
может быть либо строкой, либо другим элементом 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('hello');
// <ul><li class="first">hello</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
, то элемент добавляется последним.
// vloží prvek na první pozici a ostatní posune
$el->insert(0, Html::el('span'));
Все узлы извлекаются с помощью метода getChildren()
и удаляются с
помощью метода removeChildren()
.
Создание фрагмента документа
Если мы хотим работать с массивом узлов и нас не интересует элемент
обертки, мы можем создать фрагмент документа, передав 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"'
Важной особенностью является автоматическая защита от межсайтового скриптинга (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 или Forms использует для различения объектов,
имеющих метод __toString()
, возвращающий HTML-код. Поэтому не будет
двойного экранирования, если, например, мы перечислим объект в шаблоне,
используя {$el}
.