Go言語でWeb Assemblyに入門した

この記事は、WebAssembly Advent Calendar 2019 - Qiita の 15日目の記事です。

友人に載せられて登録してしまったので、Go言語でWasmに入門しました。

単純にチュートリアルをやるだけだとつまらないので、ちょっとだけ実用的なボタンをクリックしてメニューをトグルできるようにしました。

はじめに、github.comの通りにして作成します。

次に index.html の body タグを次の内容に変更しました。 divの子要素をWasmから操作してメニューの表示非表示を切り替えます。

<button id="menu-toggle-button">Menu</button>
<div id="menu-root"></div>

また、ついでに遷移先のHTMLファイル (about.html, links.html)も適当な内容で作成します。

その後、メニューを表示するための関数ShowMenu()と非表示にする関数HideMenu()を実装しました。

menu型の配列に入れた情報を使ってメニューのDOMを生成しています。

// リンク情報を保存するための構造体
type menu struct {
    name string
    url  string
}

var document = js.Global().Get("document")

// メニューのトグル用ボタンを取得する
func getMenuToggleButton() js.Value {
    return document.Call("getElementById", "menu-toggle-button")
}

func createElement(elementName string) js.Value {
    return document.Call("createElement", elementName)
}

func appendChild(dom js.Value, elm js.Value) js.Value {
    return dom.Call("appendChild", elm)
}

// メニューを隠す
func HideMenu(this js.Value, args []js.Value) interface{} {
    menuRoot := document.Call("getElementById", "menu-root")

    for menuRoot.Call("hasChildNodes") == js.ValueOf(true) {
        child := menuRoot.Get("lastElementChild")
        menuRoot.Call("removeChild", child)
    }

    // toggle event listener
    toggleButton := getMenuToggleButton()
    toggleButton.Call("removeEventListener", "click", js.FuncOf(HideMenu))
    toggleButton.Call("addEventListener", "click", js.FuncOf(ShowMenu))
    return nil
}

// メニューを表示する
func ShowMenu(this js.Value, args []js.Value) interface{} {
    menuList := []menu{
        {name: "home", url: "/"},
        {name: "links", url: "/links.html"},
        {name: "about", url: "/about.html"},
    }
    // make menu dom
    menuListDom := createElement("ul")
    menuListDom.Set("id", "menu-item")
    for _, e := range menuList {
        li := createElement("li")
        a := createElement("a")
        a.Set("href", e.url)
        a.Set("textContent", e.name)
        appendChild(li, a)
        appendChild(menuListDom, li)
    }
    menuRoot := document.Call("getElementById", "menu-root")
    appendChild(menuRoot, menuListDom)

    // toggle event listener
    toggleButton := getMenuToggleButton()
    toggleButton.Call("removeEventListener", "click", js.FuncOf(ShowMenu))
    toggleButton.Call("addEventListener", "click", js.FuncOf(HideMenu))
    return nil
}

次に、作成した関数をmain関数でボタンのイベントリスナーに登録します。

func main() {
    // register event
    menuToggleButton := getMenuToggleButton()
    // register call back func
    menuToggleButton.Call("addEventListener", "click", js.FuncOf(ShowMenu))

    select {}
}

ビルドは次のようにします。

GOOS=js GOARCH=wasm go build -o main.wasm

HTTPサーバは、goexecが入っていなかったのでPythonで一時的に建てました。

python -m http.server 8000

この状態で localhost:8000をブラウザで開いてボタンをクリックするとメニューの表示・非表示を切り替えられます。

f:id:tomato3713:20191215021041p:plain f:id:tomato3713:20191215021037p:plain

以上、Wasmへの入門でした。 そういえば、これを書いてるときにvimplsにsyscall/jsが見えていなかったようなんですがどうしたらいいのだろう。 明日も、Go言語とWasmらしいので何が出るのか楽しみですね。