> Docs > Html Builder

Html Builder

Html Builder is a set of APIs for building html trees.

Html Doc

To create an html document, subclass Html4Doc or Html5Doc, build elements in the constructor.

public class TestHtml extends Html5Doc
{
    public TestHtml()
    {
        _head(
            _script().src("/site.js"),
            _link().type("text/css").href("/site.css")
        );

        _body(()->
        {
            _p().id("p1").add(
                "Back to ",
                _a("home").href("/")
            ).translate(false);
        });
    }
}

The document can be serialized to chars.

new TestHtml().getContentBody(System.out::print);

// output:
<!DOCTYPE html>
<html>
    <head>
        <script src="/site.js"></script>
        <link type="text/css" href="/site.css">
    </head>
    <body>
        <p id="p1" translate="no">
            Back to <a href="/">home</a>
        </p>
    </body>
</html>

Html documents are usually built and served in response to http requests.

HttpHandler handler = request ->
{
    Html5Doc html = new TestHtml();
    return html.toResponse(200);
};

new HttpServer(handler).start(); // localhost:8080

Enable Hot Reload during development, so that changes can be seen in browser instantly.

Build Elements

A builder method creates an html element and adds it to the context parent. For example, _a() builds an <a> element.

    _div(()->
    {
        // the context parent here is the <div> element
        _a(); // add an <a> element to the <div> element

        // equivalent to
        ContextParent.add( new A() );
    });

A builder method returns the element, which can be referenced and modified later.

    INPUT email = _input().name("email");
    ...
    if(condition)
        email.value("john@example.com");

Sometimes we want to create an element without adding it to the current context parent. Use the element constructor.

    DIV logo = new DIV().class_("logo").add( children... );

    _div().id("header").add(logo, ...);
    ...
    _div().id("footer").add(..., logo);

There are builder methods for non-elements.

    _text("if n > ", 7);          //  if n &gt; 7
    _textf("%d < m < %d", 0, 7);  //  0 &lt; m &lt; 7
    _comment("start component");  //  <!-- start component -->
    _raw("<span id=");            //  <span id=

Builder methods by convention start with underscore "_" so that they are not confused with "normal" methods.

Set Attributes

Every element class has methods for standard attributes. For example, class A has methods like href(CharSequence). These methods return this for method chaining.

    _a().class_("nav").href("/"); // <a class="nav" href="/">

Attribute values are usually strings; some are integer or boolean type.

    _input().width(100).readonly(true).disabled(false); // <input width="100" readonly>
    // notice how boolean attributes `readonly` and `disabled` are rendered

Some attributes are boolean-ish; they use idiosyncratic tokens to represent true/false.

    _input().translate(true).autocomplete(false); // <input translate="yes" autocomplete="off">

For data attributes and event attributes, use data(name,value) and on(event,script).

    _div().data("x", 123).on("click", "alert()"); // <div data-x="123" onclick="alert()">

For arbitrary non-standard attributes, use attr(name, value).

    _div().attr("foo", "a&b").attr("z", 123); // <div foo="a&amp;b" z="123">

Attributes can be set/reset at any time.

    A a1 = _a("home").class_("nav").href("/").target("f1"); // <a class="nav" href="/" target="f1">home</a>
    ...
    if(condition) // change href, set id, add class, remove target
        a1.href("/sweet").id("a1").class_add("pez").target(null); // <a class="nav pez" href="/sweet" id="a1">

Add Children in a Code Block

If an element can contain children, you can pass it a code block to add children to it. The element becomes the context parent when the code block is executed.

The code block can be passed to the builder method, or to the add method of the element

    // _element( code-block )
    _div(() ->
    {
        _span();
    });

    // element.add( code-block )
    _div().id("div1").add(()->
    {
        _span();
    });

You may consider defining a shortcut for ()->{} in your IDE, since it's not very easy to type.

The code block can mix normal Java code and html builder code.

    _form().action("/query").add(()->
    {
        _text("enter 3 values: ");
        for(int i=0; i<3; i++)
            _input().name("v_"+i); // <input name="v_0"> ...
        _button("submit");
    });

    _table(()->
    {
        _tr( _th( "key" ), _th( "value" ) );
        System.getProperties().forEach( (k,v)->
                _tr( _td( k ), _td( v ) )
        );
    }).border(1);

Add Children as Method Args

If an element can contain children, you can pass an array of children to the builder method, or to the add method of the element

    _p(
        "a&b", 123, _br(),
        _a("home").href("/")
    );

    _p().id("p1").add(
        "a&b", 123, _br(),
        _a("home").href("/")
    );

A child arg can be any object, including null; if an arg is not an HtmlPiece, it will be converted to an HtmlText.

    _p("abc", 123);
    // equivalent to:
    _p( _text("abc"), _text(123) );

Some more complex examples:

    TABLE table = _table(
        _tr( _th("a"), _td(100) ),
        _tr( _th("b"), _td(200) )
    );
    if(condition)
        table.add( _tr( _th("c"), _td(300) ) );

    OL ol = _ol();
    Stream.of("aaa", "bbb", "ccc").forEach( string->
        ol.add( _li(string) )
    );

There's some magic here -- we know that _tr(...) is executed before _table(...) is executed, so the <tr> element was first created and added to a parent that's not the <table>. See ContextParent.detach() for explanation.

One annoying thing about method args is that a trailing comma is not allowed, creating a problem when cut/copy/paste lines of children. Workarounds:

    _table(
        _tr( _th("a"), _td(100) ),
        _tr( _th("b"), _td(200) ),  // <- comma
        "" // spurious last arg
    );

    _table() // add one child at a time
        .add( _tr( _th("a"), _td(100) ) )
        .add( _tr( _th("b"), _td(200) ) )
        ;

    _table(()->  // use code block instead
    {
        _tr( _th("a"), _td(100) );
        _tr( _th("b"), _td(200) );
    });

Summary of Methods

new DIV();     // constructor
_div();        // builder
div.id("1");   // attribute

// builder with children
_div( ()->{...} );
_div( child1, child2, ... );

// add children
div.add( ()->{...} );
div.add( child1, child2, ... );

See Also

Code Organization - use templates, sub-routines.

Custom Builder - create custom components