for (const auto& attr : m_attributes)
output << " " << attr.first + "=\"" << attr.second + "\"";
it'll generate incorrect HTML if an attribute value has a " character. Although early versions of HTML were kind of vague on how escaping was supposed to work, the HTML5 standard explains it in detail.I would change it so that you pass the document to the constructor of each element and the scoping of each variable effectively determines the relationships. As a 10 second example of what I mean:
HTML::document d;
HTML::body b( d );
HTML::div div( d );
HTML::p p( d );
d.text( "Hello world" );
<html><body><div><p>Hello world</p></div></body></html>
The reason I like this because it maps very well onto a C++ programmers natural understanding of the stack frame and RAII, and in addition it can be implemented with needing to store any state inside the node classes. This means that only HTML::document would need to actually allocate any memory, and it would just be a single text stream.This wouldn't create a node hierarchy in memory, so it's not a DOM like this library creates, but if you are just looking to output HTML quickly, then I think it would be easier to use and more efficient.
The document is mutable and goes through a whole sequence of states that aren't what you want?
Constructing a body object with a document argument modifies the document?
However, the helper types could be useful, and might be able to be implemented as simple aliases to a Node. Also, the reason I take the approach of appending child nodes is for representing actual HTML easier. For instance, with your .text example (at least with my limited glance on it), you can't do something such as <p>Hello, <span>world!</span> Welcome!</p>, which was actually a previous problem that I had with another version of the library.
HTML::document d;
HTML::p p( d );
d.text( "Hello, " );
{
HTML::span s( d );
d.text( "world!" );
}
d.text( " Welcome!" );
Obviously though, if you were trying to do an HTML parser as well, then my ideas are not appropriate.I prefer creating an element and then moving it into place, so a module of my application can create some structure in isolation.