The "Reverse Onion Principle" (TM) for building dynamic HTML.
The DOM is a hierarchical structure. And yet we are generating HTML in a linear fashion. We can do better!
Let's have a quick look at a PHP snippet.
<?php
if ($is_numbered_list ) {
$html = '<ol>';
}
else {
$html = '<ul>';
}
foreach ($items as $item) {
$html .= '<li>';
$html .= $item;
$html .= '</li>';
}
if ($is_numbered_list ) {
$html = '</ol>';
}
else {
$html = '</ul>';
}
?>
Why is this bad?
Just have a look how far away closing and opening tag are in the code!
Onion style
Let's try to do it differently.
<?php
$items_html = '';
foreach ($items as $item) {
$items_html = '<li>' . $item . '</li>';
}
if ($is_numbered_list) {
$html = '<ol>' . $items_html . '</ol>';
}
else {
$html = '<ul>' . $items_html . '</ul>';
}
?>
Much better, isn't it. Whenever we open a tag, we are closing it in the exact same place.
We can even go one step further:
<?php
$items_html = '';
foreach ($items as $item) {..}
if ('' !== $items_html)) {
$html = '<ul>' . $items_html . '</ul>';
}
?>
So we only print the wrapper, if there is actual content.
We could not have done that if we wanted to print the '
<
ul>' at the beginning.
What about templates?
(I am going to use phptemplate for these examples, but you can do similar things with other template engines)
<div><div>
<?php if ($is_numbered_list): ?><ol><?php else ?><ul><?php endif; ?>
<?php foreach ($items as $item): ?>
<li><?php print $item; ?></li>
<?php endforeach; ?>
<?php if ($is_numbered_list): ?></ol><?php else ?></ul><?php endif; ?>
</div></div>
Beh. Templates may be nice for people who prefer HTML over "real code", but they don't make it that much easier to see which closing tag belongs to which opening tag. Especially with conditionals in place. The IDE can help, but I have seen a number of templates where this was not as obvious.
I have even seen worse:
$html-top.tpl.php
:
<html><body>
$html-bottom.tpl.php
:
</body></html>
So the opening tag was in a different template than the closing tag, making it even more intransparent.
Onion-style templates
It is not as straightforward as building strings in PHP. But we can do this in templates with output buffering.
<?php ob_start(); ?>
<?php foreach ($items as $item): ?>
<li>..</li>
<?php endforeach; ?>
<?php $contents = ob_get_clean(); ?>
<?php if (!empty($contents)): ?>
<ul><?php print $contents: ?></ul>
<?php endif; ?>
Caveat: If we are already doing this, we might as well split it up into separate templates instead. But there might be situations where we don't want this.
I'd say it is definitely a technique we can add to our toolkit, and see how it performs in practice.