HTML Elements
The Nette\Utils\Html class is a helper for generating HTML code that prevents Cross Site Scripting (XSS) vulnerability.
It works in such a way that its objects represent HTML elements, we set their parameters and let them render:
$el = Html::el('img'); // creates <img> element
$el->src = 'image.jpg'; // sets src attribute
echo $el; // prints '<img src="image.jpg">'
Installation:
composer require nette/utils
All examples assume the following class alias is defined:
use Nette\Utils\Html;
Creating an HTML Element
The element is created using the method Html::el()
:
$el = Html::el('img'); // creates <img> element
In addition to the name, you can enter other attributes in the HTML syntax:
$el = Html::el('input type=text class="red important"');
Or pass them as an associative array to the second parameter:
$el = Html::el('input', [
'type' => 'text',
'class' => 'important',
]);
To change and return an element name:
$el->setName('img');
$el->getName(); // 'img'
$el->isEmpty(); // true, as <img> is void element
HTML Attributes
You can set and get individual HTML attributes in three ways, it's up to you who you like more. The first is through the properties:
$el->src = 'image.jpg'; // sets src attribute
echo $el->src; // 'image.jpg'
unset($el->src); // removes attribute
// or $el->src = null;
The second way is to call methods that, in contrast to setting properties, we can chain together:
$el = Html::el('img')->src('image.jpg')->alt('photo');
// <img src="image.jpg" alt="photo">
$el->alt(null); // removes attribute
And the third way is most talkative:
$el = Html::el('img')
->setAttribute('src', 'image.jpg')
->setAttribute('alt', 'photo');
echo $el->getAttribute('src'); // 'image.jpg'
$el->removeAttribute('alt');
In bulk, attributes can be set with addAttributes(array $attrs)
and deleted with
removeAttributes(array $attrNames)
.
The value of an attribute does not have to be only a string, logical values for logical attributes can also be used:
$checkbox = Html::el('input')->type('checkbox');
$checkbox->checked = true; // <input type="checkbox" checked>
$checkbox->checked = false; // <input type="checkbox">
An attribute can also be an array of tokens, which are listed separated by spaces, which is suitable for CSS classes, for example:
$el = Html::el('input');
$el->class[] = 'active';
$el->class[] = null; // null is ignored
$el->class[] = 'top';
echo $el; // '<input class="active top">'
An alternative is an associative array, where the values say whether the key should be listed:
$el = Html::el('input');
$el->class['active'] = true;
$el->class['top'] = false;
echo $el; // '<input class="active">'
CSS styles can be written in the form of associative arrays:
$el = Html::el('input');
$el->style['color'] = 'green';
$el->style['display'] = 'block';
echo $el; // '<input style="color: green; display: block">'
We have now used properties, but the same can be done using the methods:
$el = Html::el('input');
$el->style('color', 'green');
$el->style('display', 'block');
echo $el; // '<input style="color: green; display: block">'
Or even in the most talkative way:
$el = Html::el('input');
$el->appendAttribute('style', 'color', 'green');
$el->appendAttribute('style', 'display', 'block');
echo $el; // '<input style="color: green; display: block">'
One last thing: the method href()
can make it easier to compose query parameters in a URL:
echo Html::el('a')->href('index.php', [
'id' => 10,
'lang' => 'en',
]);
// '<a href="index.php?id=10&lang=en"></a>'
Data Attributes
Data attributes have special support. Because their names contain hyphens, access via properties and methods is not so elegant,
so there is a method data()
:
$el = Html::el('input');
$el->{'data-max-size'} = '500x300'; // not so elegant
$el->data('max-size', '500x300'); // is elegant
echo $el; // '<input data-max-size="500x300">'
If the value of the data attribute is an array, it is automatically serialized to JSON:
$el = Html::el('input');
$el->data('items', [1,2,3]);
echo $el; // '<input data-items="[1,2,3]">'
Element Content
The inner content of the element is set by the setHtml()
or setText()
methods. Use the first one only
if you know that you are reliably passing a secure HTML string in the parameter.
echo Html::el('span')->setHtml('hello<br>');
// '<span>hello<br></span>'
echo Html::el('span')->setText('10 < 20');
// '<span>10 < 20</span>'
Conversely, the inner content is obtained by methods getHtml()
or getText()
. The second one removes
tags from the HTML output and converts the HTML entities to characters.
echo $el->getHtml(); // '10 < 20'
echo $el->getText(); // '10 < 20'
Child Nodes
The inner content of an element can also be an array of children. Each of them can be either a string or another
Html
element. They are inserted using addHtml()
or addText()
:
$el = Html::el('span')
->addHtml('hello<br>')
->addText('10 < 20')
->addHtml( Html::el('br') );
// <span>hello<br>10 < 20<br></span>
Another way to create and insert a new Html
node:
$ul = Html::el('ul');
$ul->create('li', ['class' => 'first'])
->setText('hello');
// <ul><li class="first">hello</li></ul>
You can work with nodes as if they were array items. So access the individual ones using square brackets, count them with
count()
and iterate over them:
$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
A new node can be inserted at a specific position using insert(?int $index, $child, bool $replace = false)
. If
$replace = false
, it inserts the element at the $index
position and moves the others. If
$index = null
, it will append an element at the end.
// inserts the element in the first position and advances the others
$el->insert(0, Html::el('span'));
All nodes are returned by method getChildren()
and removed by method removeChildren()
.
Creating a Document Fragment
If you want to work with an array of nodes and are not interested in the wrapping element, you can create a so-called
document fragment by passing null
instead of the element name:
$el = Html::el(null)
->addHtml('hello<br>')
->addText('10 < 20')
->addHtml( Html::el('br') );
// hello<br>10 < 20<br>
Methods fromHtml()
and fromText()
offer a faster way to create a fragment:
$el = Html::fromHtml('hello<br>');
echo $el; // 'hello<br>'
$el = Html::fromText('10 < 20');
echo $el; // '10 < 20'
Generating HTML Output
The easiest way to generate an HTML element is to use echo
or cast an object to (string)
. You can
also prints opening or closing tags and attributes separately:
$el = Html::el('div class=header')->setText('hello');
echo $el; // '<div class="header"></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"'
An important feature is the automatic protection against Cross Site Scripting (XSS). All attribute values
or content inserted using setText()
or addText()
is reliably escaped:
echo Html::el('div')
->title('" onmouseover="bad()')
->setText('<script>bad()</script>');
// <div title='" onmouseover="bad()'><script>bad()</script></div>
Conversion HTML ↔ Text
You can use the static method htmlToText()
to convert HTML to text:
echo Html::htmlToText('<span>One & Two</span>'); // 'One & Two'
HtmlStringable
The Nette\Utils\Html
object implements the Nette\HtmlStringable
interface, which, for example, Latte
or forms use to distinguish objects that have a method __toString()
that returns HTML code. So double escaping does
not occur if, for example, we print the object in the template using {$el}
.