はじめに
最近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にすると下記となります。
<!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の状態を反転させれば良いでしょう。以上を実装すると下記となります。
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"の追加するしないを制御すればよいです。