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&amp;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 &lt; 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 &lt; 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 &lt; 20<br></span>

Another way to create and insert a new Html node:

$el = Html::el('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 &lt; 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 &lt; 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()'>&lt;script&gt;bad()&lt;/script&gt;</div>

Conversion HTML ↔ Text

You can use the static method htmlToText() to convert HTML to text:

echo Html::htmlToText('<span>One &amp; 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}.

version: 4.0 3.x 2.x