BulmaのハンバーガーメニューをElmでトグルする

# はじめに

最近Elmを触り始めました。せっかくWebサイトを作るのならリッチなほうがいいですよね。ということでCSSフレームワークとしてElmと親和性の高そうなBulmaをつかうことにしました。その際にハンバーガーメニューでちょっと迷いました。その際のメモです。

# Bulma、Elmとは?

ElmはJavaScript にコンパイルできる関数型プログラミング言語です。 詳しい説明は公式ドキュメントをよみましょう。日本語訳もあります。

BulmaはCSSフレームワークの1つです。フロントエンドのデザインのフレームワークというとBootstrapが有名です。しかし、BootstrapはJavaScriptを内包しているため、自分でJavaScriptやElmでDOMを操作するような際は干渉してしまい不適です。一方、BulmaはCSSのみで構成されているためElmと干渉するようなことがありません。詳しいことは公式ドキュメントを(略)

# 実装

まずhtmlの概要を書いていきます。重要になるのがナビゲーションバーです。
公式ドキュメントのナビゲーションバーの説明( https://bulma.io/documentation/components/navbar/#navbar-burger )によれば

> The navbar-burger is a hamburger menu that only appears on touch devices. It has to appear as the last child of navbar-brand. It has to contain three empty span tags in order to visualize the hamburger lines or the cross (when active).

とあります。つまり「ハンバーガーメニューはタッチデバイスのときのみに現れ、"navbar-brand"の最後の子要素であるひつようがあり、3つのspanタグが必要」ということですね。

また、ハンバーガーメニューに表示する内容は"navbar-menu"で指定できます。例のごとく公式ドキュメントを読んでみると

> The navbar-menu is the counterpart of the navbar brand. As such, it must appear as a direct child of navbar, as a sibling of navbar-brand.

とあり、navbar-menuは navbar-brandの子要素である必要があるとわかります。これらを踏まえてhtmlにすると下記となります。

```html
<!DOCTYPE html>
<html>

    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Bulma Sample</title>
        <link rel="stylesheet" href="https://unpkg.com/bulma@0.8.2/css/bulma.min.css" />
    </head>

    <body>

        <nav class="navbar is-white">
            <div class="container">
                <div class="navbar-brand">
                    <a class="navbar-item brand-text" href="../index.html">
                        burger Sample
                    </a>
                    <div class="navbar-burger burger" data-target="navMenu">
                        <span></span>
                        <span></span>
                        <span></span>
                    </div>
                </div>
                <div id="navMenu" class="navbar-menu">
                    <div class="navbar-start">
                        <a class="navbar-item" href="#">
                            AAA
                        </a>
                        <a class="navbar-item" href="#">
                            BBB
                        </a>
                        <a class="navbar-item" href="#">
                            CCC
                        </a>
                    </div>
                </div>
            </div>
        </nav>

    </body>
</html>

```


これでウィンドウサイズを変更するとナビゲーションメニューやハンバーガーメニューが現れたり、消えたりします。しかし、ハンバーガーメニューをクリックしても内容が現れたりしません。これはBulmaはCSSのみで構成されているため、具体的に動かしたりの処理実装されていないためです。したがって自分で実装します。

ElmではhtmlをElmの関数として表現します。そのためにまずはHtmlをHtmlを表現するElmの型であるHtml Msgに変換する必要があります。このHtmlからElmへの変換はhttp://mbylstra.github.io/html-to-elm/ というサイトが便利です。

また、ハンバーガーメニューの制御には2つの段階があります。まずハンバーガーメニューを「三」から「×」マークにする、もうひとつが開閉を制御するです。前者は"navbar-burger"のclassに"is-active"を、後者は"navbar-menu"に"is-active"を指定すれば実現できます。これをハンバーガーメニューをクリックしたときにトグルするようにすればいいですね。これはElmのmodelとしてはトグルの状態を管理する型、MsgはToggleとし、Toggleが発行されたらupdateでmodelの状態を反転させれば良いでしょう。以上を実装すると下記となります。

```elm
module Main exposing (Model, Msg(..), init, main, update, view)

import Browser
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)


main =
    Browser.sandbox { init = init, update = update, view = view }



-- MODEL


type alias Model =
    Bool


init : Model
init =
    False



-- UPDATE


type Msg
    = Toggle


update : Msg -> Model -> Model
update msg model =
    case msg of
        Toggle ->
            not model



-- VIEW


view : Model -> Html Msg
view model =
    div []
        [ nav [ class "navbar is-white" ]
            [ div [ class "container" ]
                [ div [ class "navbar-brand" ]
                    [ a [ class "navbar-item brand-text", href "../index.html" ]
                        [ text "burger Sample" ]
                    , div
                        [ class "navbar-burger burger"
                        , class
                            (if model then
                                "is-active"

                             else
                                "burger"
                            )
                        , attribute "data-target" "navMenu"
                        , onClick Toggle
                        ]
                        [ span []
                            []
                        , span []
                            []
                        , span []
                            []
                        ]
                    ]
                , div
                    [ class "navbar-menu"
                    , class
                        (if model then
                            "is-active"

                         else
                            ""
                        )
                    , id "navMenu"
                    ]
                    [ div [ class "navbar-start" ]
                        [ a [ class "navbar-item", href "#" ]
                            [ text "AAA" ]
                        , a [ class "navbar-item", href "#" ]
                            [ text "BBB" ]
                        , a [ class "navbar-item", href "#" ]
                            [ text "CCC" ]
                        ]
                    ]
                ]
            ]
        ]
```

これでハンバーガーメニューを押したときに開閉ができるようになりましたね。

# まとめ

今回はBulmaのハンバーガーメニューをElmから操作する方法について紹介しました。Bulmaのハンバーガーメニューの制御は"navbar-burger"および"navbar-menu"に"is-active"をつけるかつけないかでできます。Elm側ではトグル状態を表現したモデルを用意してその状態に応じて"is-active"の追加するしないを制御すればよいです。