> Docs > Html Builder
Html Builder is a set of APIs for building html trees.
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.
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 > 7 _textf("%d < m < %d", 0, 7); // 0 < m < 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.
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&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">
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);
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) ); });
new DIV(); // constructor _div(); // builder div.id("1"); // attribute // builder with children _div( ()->{...} ); _div( child1, child2, ... ); // add children div.add( ()->{...} ); div.add( child1, child2, ... );
Code Organization - use templates, sub-routines.
Custom Builder - create custom components