func (s HTML) On(event string, rootUpdateFunc, updateFunc, actionFunc interface{}) HTML {
	id := s.ID()
	s.initFuncs = append(s.initFuncs, func() {
		el := dom.GetWindow().Document().GetElementByID(id)
		if el == nil {
			log.Printf("unable to find DOM element id=%q", id)
			return
		}
		el.AddEventListener(event, false, func(de dom.Event) {
			go func() {
				u := reflect.ValueOf(updateFunc)
				a := reflect.ValueOf(actionFunc)
				args := []reflect.Value{a}
				if reflect.TypeOf(updateFunc).NumIn() == 2 {
					var e interface{} = de
					args = append(args, reflect.ValueOf(e))
				}
				newModel := u.Call(args)
				if view, ok := newModel[0].Interface().(Viewer); ok {
					Render(view, rootUpdateFunc)
				}
			}()
		})
	})
	return s
}
On adds an event handler to an HTML element.
package html
import (
	"fmt"
	"html/template"
	"log"
	"reflect"
	"strconv"
	"honnef.co/go/js/dom"
)
const domNode = "go-frp"
var nextID int  
type Viewer interface {
	View(rootUpdateFunc, updateFunc interface{}) HTML
}
func Render(view Viewer, rootUpdateFunc interface{}) {
	v := view.View(rootUpdateFunc, nil)
	str, initFuncs := v.Render()
	dom.GetWindow().Document().GetElementByID(domNode).SetInnerHTML(str)
	for _, initFunc := range initFuncs {
		initFunc()
	}
}
type HTML struct {
	tag       string
	id        string
	props     [][]string
	styles    [][]string
	body      string
	elems     []HTML
	initFuncs []func()
}
func (s HTML) Render() (string, []func()) {
	var result string
	var initFuncs []func()
	if s.tag != "" {
		result = "<" + s.tag
		for _, v := range s.props {
			result += fmt.Sprintf(" %s=%q", v[0], template.HTMLEscapeString(v[1]))
		}
		var styles string
		for _, v := range s.styles {
			styles += fmt.Sprintf("%s:%s;", v[0], template.HTMLEscapeString(v[1]))
		}
		if styles != "" {
			result += fmt.Sprintf(" style=%q", styles)
		}
		if s.body == "" && len(s.elems) == 0 {
			result += "/"
		}
		result += ">"
	}
	for _, v := range s.elems {
		str, ifs := v.Render()
		result += str
		initFuncs = append(initFuncs, ifs...)
	}
	initFuncs = append(initFuncs, s.initFuncs...)
	result += template.HTMLEscapeString(s.body)
	if s.tag != "" && !(s.body == "" && len(s.elems) == 0) {
		result += "</" + s.tag + ">"
	}
	return result, initFuncs
}
func (s HTML) Props(props [][]string) HTML {
	s.props = props
	return s
}
func (s HTML) Style(styles [][]string) HTML {
	s.styles = styles
	return s
}
func (s *HTML) ID() string {
	if s.id != "" {
		return s.id
	}
	for _, p := range s.props {
		if p[0] == "id" || p[0] == "ID" {
			s.id = p[1]
			return s.id
		}
	}
	s.id = strconv.Itoa(nextID)
	s.props = append(s.props, []string{"id", s.id})
	nextID++
	return s.id
}