No Description

Darko Luketic 4b41a19dcc added go.mod go.sum; moved to 1 year ago
elements 4b41a19dcc added go.mod go.sum; moved to 1 year ago
examples 4b41a19dcc added go.mod go.sum; moved to 1 year ago
spa 4b41a19dcc added go.mod go.sum; moved to 1 year ago
test 4b41a19dcc added go.mod go.sum; moved to 1 year ago
.gitignore 75b973a4e1 remove build 5 years ago
LICENSE d1289f4e87 Initial commit 5 years ago 4b41a19dcc added go.mod go.sum; moved to 1 year ago
attr_path.go 661d36c4a1 Add routerlist, rename dom-switch attrs 5 years ago
data_bindings.go 80972181c1 Add tests, README and example for dom-repeat 5 years ago
element.go 7d8ac66b94 Fix addEventListener, md-layout beginning 5 years ago
event.go 0a0a62bdce removing not needed code 5 years ago
go.mod 4b41a19dcc added go.mod go.sum; moved to 1 year ago
go.sum 4b41a19dcc added go.mod go.sum; moved to 1 year ago
golymer.go 0d33914062 Add md-icon 4 years ago
golypher.png 1f1a374510 add golypher 5 years ago
template.go b4f067c236 Add template element support and update README 5 years ago
text_manipulation.go 794f4d9499 Minimalize package usage 5 years ago



Create HTML custom elements with go (gopherjs)

With golymer you can create your own HTML custom elements, just by registering a go struct. The content of the shadowDOM has automatic data bindings to the struct fields. Read an blog post.

Contribution of all kind is welcome. Tips for improvement or api simplification also :)

package main

import ""

var myAwesomeTemplate = golymer.NewTemplate(`
	:host {
		background-color: blue;
		width: 500px;
		height: [[FooAttr]]px;
<input type="text" value="{{BarAttr}}"/>

type MyAwesomeElement struct {
	FooAttr int
	BarAttr string
	privateProperty float64

func NewMyAwesomeElement() *MyAwesomeElement {
	e := &MyAwesomeElement{
		FooAttr: 800,
	return e

func main() {
	//pass the element constructor to the Define function
	err := golymer.Define(NewMyAwesomeElement)
	if err != nil {

Then just run $ gopherjs build, import the generated script to your html <script src="my_awesome_element.js"></script> and you can use your new element

<my-awesome-element foo-attr="1" bar-attr="hello"></my-awesome-element>

define an element

To define your own custom element, you must create an struct that embeds the golymer.Element struct and a function that is the constructor for the struct. Then add the constructor to the golymer.Define function. This is an minimal example.:

type MyElem struct {

func NewMyElem() *MyElem {
	return new(MyElem)

func init() {
	err := golymer.Define(NewMyElem)
	if err != nil {

The struct name, in CamelCase, is converted to the kebab-case. Because html custom elements must have at least one dash in the name, the struct name must also have at least one "hump" in the camel case name. (MyElem -> my-elem). So, for example, an struct named Foo will not be defined and the Define function will return an error.

Also the constructor fuction must have a special shape. It can't take no arguments and must return a pointer to an struct that embeds the golymer.Element struct.

element attributes

golymer creates attributes on the custom element from the exported fields and syncs the values.

type MyElem struct {
	ExportedField string
	unexportedField int
	Foo float64
	Bar bool

Exported fields have attributes on the element. This enables to declaratively set the api of the new element. The attributes are also converted to kebab-case.

<my-elem exported-field="value" foo="3.14" bar="true"></my-elem>

HTML element attributes can have just text values, so golymer parses these values to convert it to the field types with the strconv package.

lifecycle callbacks

golymer.Element implemets the golymer.CustomElement interface. It's an interface for the custom elements lifecycle in the DOM.

ConnectedCallback() called when the element is connected to the DOM. Override this callback for setting some fields, or spin up some goroutines, but remember to call the golymer.Element also (myElem.Element.ConnectedCallback()).

DisconnectedCallback() called when the element is disconnected from the DOM. Use this to release some resources, or stop goroutines.

AttributeChangedCallback(attributeName string, oldValue string, newValue string, namespace string) this callback called when an observed attribute value is changed. golymer automatically observes all exported fields. When overriding this, also remember to call golymer.Element callback (myElem.Element.AttributeChangedCallback(attributeName, oldValue, newValue, namespace)).


The function golymer.NewTemplate will create a new template element, from which the new custom element's shadowDOM will be instantiated. It is better to use the template element for this, and not innerHTML, because the browser must parse the html only once.

If you want to create an custom element with a template, you must set the elements template in the constructor with the SetTemplate function.

var myTemplate = golymer.NewTemplate(`<h1>Hello golymer</h1>`)

func NewMyElem() *MyElem {
	e := new(MyElem)
	return e

The element will then have an shadowDOM thats content will be set from the Template element at the connectedCallback.

one way data bindings

golymer has build in data bindings. One way data bindings are used for presenting an struct field's value. For defining an one way databinding you can use double square brackets with the path to the field ([[Field]] or [[subObject.Field]]) Eg:


Where the host struct has an Text field. Or the name in brackets can be an path to an fielt subObject.subSubObject.Field. The field value is then converted to it's string representation. One way data bindings can be used in text nodes, like in the example above, and also in element attributes eg. <div style="display: [[MyDisplay]];"></div>

Every time the fields value is changed, the template will be automaticaly changed. Changing the Text fields value eg myElem.Text = "foo" also changes the <p> element's innerHTML.

two way data bindings

Two way data bindings are declared with two curly brackets ({{Field}} or {{subObject.Field}}) and work only in attributes of elements in the template. So every time the elements attribute is changed, the declared struct field will also be changed. golymer makes also an workaround for html input elements, so it is posible to just bind to the value attribute.

<input id="username" name="username" type="text" value="{{Username}}">

Changing elem.Username changes the input.value, and also changing the input.value or the value attribute document.getElementById("username").setAttribute("value", "newValue") or the user adds some text, the elem.Username will be also changed.

It's also possible to pass complex data structures to the sub element with two way data bindings.

type User struct {
	ID   int
	Name string

type ElemOne struct {
	Usr  *User
type ElemTwo struct {
	Usr  *User

ElemOne template:

	<elem-two usr="{{Usr}}"></elem-two>

This will keep the elemOne.Usr and elemTwo.Usr the same pointer to the same object.

connecting to events

Connecting to the events of elements can be done with the javascript addEventListener function, but it is also possible to connect some struct method with an on-<eventName> attribute.

<button on-click="ButtonClicked"></button>

Event handlers get the event as a pointer to golymer.Event

func (e *MyElem) ButtonClicked(event *golymer.Event) {
	print("the button was clicked!")

custom events

golymer adds the DispatchEvent method so you can fire your own events.

event := golymer.NewEvent(
		"detail": map[string]interface{}{
			"data": "foo",
		"bubbles": true,

and these events can be also connected to:

<my-second-element on-my-event="MyCustomHandler"></my-second-element>


On changing an fields value, you can have an observer, that will get the old and new value of the field. It must just be an method with the name: Observer<FieldName>. eg:

func (e *MyElem) ObserverText(oldValue, newValue string) {
	print("Text field changed from", oldValue, "to", newValue)


golymer scans the template and checks the id of all elements in it. The id will then be used to map the children of the custom element and can be accessed from the Childen map (map[string]*js.Object). Attribute id cannot be databinded (it's value must be constant).

var myTemplate = golymer.NewTemplate(`
<h1 id="heading">Heading</h1>
<my-second-element id="second"></my-second-element>
<button on-click="Click">click me</button>

func (e *MyElem) Click(event *golymer.Event) {
	secondElem := e.Children["second"].Interface().(*MySecondElement)

golymer also populates fields of the custom element from the id. If the element's id is equal to the field name and if it's type is the same as the element in template. eg:

var tmpl = golymer.NewTemplate(`
<my-second-element id="second"></my-second-element>

type MyElem struct {
	second *MySecondElement //this field will after ConnectedCallback have an pointer to the `<my-second-element>` in the template

type assertion

It is possible to type assert the node object to your custom struct type. With selecting the node from the DOM directly

myElem := js.Global.Get("document").Call("getElementById", "myElem").Interface().(*MyElem)

and also from the Children map

secondElem := e.Children["second"].Interface().(*MySecondElement)

element creation

Calling your custom element's constructor creates you just the struct type. The node must be created by the DOM. So you must create your registered element with document.createElement:

myElem := js.Global.Get("document").Call("createElement", "my-elem").Interface().(*MyElem)

Or you can use the helper function from golymer:

myElem := golymer.CreateElement("my-elem").(*MyElem)

golymer elements

The subpackage golymer elements contains common elemets needed in building websites.

Like some material design elements and also some common declarative dom manipulation elements.

But it's still an work in progress. The collection isn't complete, but elements that are already complete are functioning and usable.


The subpackage spa contains utils/helpers for building single page applications.