riot.jsでカスタムタグを挿入してFoundationとかを崩してしまわないためのメモ

はじめに

私は手軽に綺麗なサイトを作りたいときに[Foundation](http://foundation.zurb.com/sites/docs/)を使うことが多いです。
はじめから構造を意識して書くことで、HTML書いて、JSかいて、CSS書いて、戻ってクラスつけて、とかの作業が少なくなるからです。

```html
<div class="user-info-box-area row small-12 small-up-3 medium-up-3 large-up-4">
    <div class="user-info-box column column-block align-center">
        <img src="//placehold.it/600x600" class="thumbnail" alt="">
        <div class="row align-spaced align-middle">
            <div class="row align-center">
                <span class="user-name">太郎</span>
            </div>
        </div>
    </div>
    <div class="user-info-box column column-block align-center">
        <img src="//placehold.it/600x600" class="thumbnail" alt="">
        <div class="row align-spaced align-middle">
            <div class="row align-center">
                <span class="user-name">次郎</span>
            </div>
        </div>
    </div>
    <div class="user-info-box column column-block align-center">
        <img src="//placehold.it/600x600" class="thumbnail" alt="">
        <div class="row align-spaced align-middle">
            <div class="row align-center">
                <span class="user-name">三郎</span>
            </div>
        </div>
    </div>
</div>
```

これはこんな風になります（ブラウザ幅によって一行３〜６個のグリッドになります）
![範囲を選択_Shutter_20170119_0007.png](https://qiita-image-store.s3.amazonaws.com/0/58443/e0908493-7163-310d-f6af-1e3fdda30858.png)

多少冗長ではありますが、HTMLメインで書き進められるのが楽ですね。

## 問題

ところで、このボックス一つ一つをriot.jsのカスタムタグにすると便利かなと思いました。

```html:user_info_box_area.tag
<user-info-box>

    <div class="user-info-box column column-block align-center main"
         id={ userUuid } onclick={ toggleSelected }>
        <img src={ imageSrc } class="thumbnail" alt="">
        <div class="row align-spaced align-middle">
            <div class="row align-center">
                <span class="user-name">{ userName }</span>
            </div>
        </div>
    </div>

<script type="text/coffeescript">

    @imageSrc = "//placehold.it/600x600"
    @userUuid = opts.userUuid
    @userName = opts.userName

    @changeImageSrc = (newImageSrc) ->
        @imageSrc = newImageSrc
        @update()

    @changeUserName = (newUserName) ->
        @userName = newUserName
        @update()

    @removeThis = ->
        @unmount(false)

</script>

</user-info-box>


<user-info-box-area>
    
    <div class="user-info-box-area row small-12
               small-up-3 medium-up-3 large-up-4 xlarge-up-5 xxlarge-up-6
               align-center">
        
    </div>

    <script type="text/coffeescript">

        @selectedUuid = null

        @addUserInfoBox = (userUuid, userName) ->
            userInfoBoxTag = document.createElement('user-info-box')
            $(".user-info-box-area").append userInfoBoxTag
            # オプション変数を渡しながらmount
            dom = riot.mount userInfoBoxTag,
                    {
                        userName: userName,
                        userUuid: userUuid,
                    }
            return dom[0]

    </script>

</user-info-box-area>
```

RailsだからってApp使いまくってたり良い実装とは言えないですがそこはおいておいて

これで

```coffeescript
# domを格納
tag = document.createElement('user-info-box-area')
# domをはりつけ
$("#mount-position").append tag
# 中身のコンパイル
App.userInfoBoxArea = (riot.mount tag)[0]
# 子nodeの作成
App.userInfoBoxArea.addUserInfoBox("ichiro", "イチロー")
```

とやればいい感じに子要素ができるはず

ですが、

実際にマウントするとこのようになります。

```html
<user-info-box-area>
    <div class="user-info-box-area ... ">
        <user-info-box>
            <div class="user-info-box ... ">
```

これではあいだあいだになんの `class` 属性もないタグ(しかも標準タグじゃない)が出てしまうのでFoundationのスタイルが壊れてしまいます。具体的にはまず横並びになりません。つらい


## 改善

公式に微妙にわかりづらく？書いてありました。
http://riotjs.com/v2/ja/guide/#html-2

riotjsの機能を使いつつ、独自タグを使わずに記述をすることができます。

よーしやるぞー

```coffeescript
# DIV要素を格納
tag = document.createElement('div')
# riotタグとしてのフラグをつける
tag.setAttribute("data-is", "user-info-box-area")
# domをはりつけ
$("#mount-position").append tag
# 中身のコンパイル
App.userInfoBoxArea = (riot.mount tag, "user-info-box-area")[0]
# 子nodeの作成
App.userInfoBoxArea.addUserInfoBox("ichiro", "イチロー")
```

公式ドキュメントで述べられていない点ですが、
`riot.mount` は引数をいくつか取ることができ、

```coffeescript
riot.mount tag, "user-info-box-area"
```

と渡した際は、ひとつ目のDOMだけをふたつ目のRiotjsタグとしてコンパイルする、という感じの挙動をしました。（`riot.mount tag` ではちゃんとマウントされず、 `riot.mount "user-info-box-area"` では `"user-info-box-area"` なタグ全てが再マウントされてしまいました）

で、結果を見るとこうなります。

```html
<div>
    <div class="user-info-box-area ... ">
        <div>
            <div class="user-info-box ... ">
```

まぁ当然ながらまだ崩れます

## 改善２

カスタムタグ自体に属性をつけてしまえばいいのではないか

というわけでこうしました

```html:user_info_box_area.tag
<user-info-box class="user-info-box column column-block align-center main"
               id={ userUuid } onclick={ toggleSelected }>

    <img src={ imageSrc } class="thumbnail" alt="">
    <div class="row align-spaced align-middle">
        <div class="row align-center">
            <span class="user-name">{ userName }</span>
        </div>
    </div>

    <script type="text/coffeescript">

        @imageSrc = "//placehold.it/600x600"
        @userUuid = opts.userUuid
        @userName = opts.userName

        @changeImageSrc = (newImageSrc) ->
            @imageSrc = newImageSrc
            @update()

        @changeUserName = (newUserName) ->
            @userName = newUserName
            @update()

        @removeThis = ->
            @unmount(false)

    </script>

</user-info-box>


<user-info-box-area class="user-info-box-area row small-12
               small-up-3 medium-up-3 large-up-4 xlarge-up-5 xxlarge-up-6
               align-center">

    <script type="text/coffeescript">

        @selectedUuid = null

        @addUserInfoBox = (userUuid, userName) ->
            userInfoBoxTag = document.createElement('div')
            userInfoBoxTag.setAttribute("data-is", "user-info-box")
            $(".user-info-box-area").append userInfoBoxTag
            # オプション変数を渡しながらmount
            dom = riot.mount userInfoBoxTag, "user-info-box",
                    {
                        userName: userName,
                        userUuid: userUuid,
                    }
            return dom[0]

    </script>

</user-info-box-area>
```

こうするとカスタムタグのうち `user-info-box-area` の部分だけが `div` になり望んだ通りの結果が出力されます。

```html
<div class="user-info-box-area ... ">
    <div class="user-info-box ... ">
```

やったね。

この方法ドキュメントに書いてあったような気がしたのですが、見返してみたら見つかりませんでした。見落としているのかもしれません。