html要素のidにコロンを指定するのは注意！

そのidを検索する際の処理で躓く可能性があります、っていう話です。

# はじめに
実績がない初心者なりに、アプリコンテストを目指したアプリの試作をしている今日此頃。

ところが、想定外していなかったミスで、数時間も食う事件が発生しました。

今回はその事件の原因について、自分への戒めも兼ねて記します。


# 事件背景
先述したアプリについて、ざっくりいうと、下のような構成です。

- 外部API
- フレームワーク
    - vuejs
- レスポンシブデザイン向けライブラリ
    - bootstrap
- 地図ライブラリ
    - leafletjs

要は外部APIからデータを取得し、そのデータと地図を連携させ、更にそれをbootstrapで良い感じに見せたい！というもの。

事件は、bootstrapのcollapse(折りたたみ)を使っている時に起こりました。

# 事件詳細
詳細なデータを受け取り、それをアコーディオン表示をさせようと、サンプルをほぼそのまま参考に、カード部分をコンポーネントにしました。vuejsが分かる人向けのイメージは以下。

```html:Detail.vue
<template>
    <div id="detail">
        <div
        class="accordion"
        :id="cardParent"
        >
            <Card
            v-for="data in jsonData"
            :key="data['@id']"
            :cardId="data['@id']"
            :title="data['title']"
            :parentId="cardParent"
            />
        </div>
    </div>
</template>
```

```html:Card.vue
<template>
    <div
    class="card"
    >
        <div
        class="card-header"
        :id="this.headerId"
        >
            <button
            class="btn btn-link btn-block text-left collapsed"
            type="button"
            data-toggle="collapse"
            :data-target="'#'+this.bodyId"
            aria-expanded="false"
            :aria-controls="this.bodyId"
            >
                {{this.title}}
            </button>
        </div>
        <div
        :id="this.bodyId"
        class="collapse card-body"
        :data-parent="'#'+this.parentId"
        >
            <p>テスト</p>
            <!--
                外部APIから取得したデータの
                詳細をここに記す
            -->
        </div>
    </div>
</template>
<script>
```

カードをどれだけ作るかは、外部APIのデータやユーザーの操作次第。なので、カードやその内部要素のidは、コンポーネント内では決められません。

そこで、外部APIのデータのidを元に、カードや内部要素のidを決めることに。

idが`id00001`なら、カードは`id00001`、内部要素は`id00001_header`とするように、コードを記述しました。

(vuejsが分かる人向けに言うと、Card.vueではcardIdをプロパティに受け取り、それに基づき、算出プロパティで内部要素用idをセットしています)

サンプルほぼそのままなので、これで問題はないはず！と動かした所、最初は問題なく表示されているように見えました。

でも、何かがおかしい。よく見ると、アコーディオン表示が開けない。

しかし、サンプルコードを見比べても、動作しているhtmlを見ても、何も問題がない。

加えて、`display : none`できちんとDOMは描画されている。

一体、何だ！？何が原因なんだ！？！？！？


---------------------------------------------




# 犯人
結局、上記のミスに気づくのに数時間を費やしてしまいました。

まだ扱いに慣れていないbootstrapばかりに気を取られて、原因は全く関係のない所にあったことに気づくのが遅かった為です。

実は、***外部APIのデータのidをもとに、カードのidとした***のが、犯人でした。

外部APIからのjsonデータ(のid)に、記号、コロンなどが含まれていました。

このidを、直接カードのidに指定していたのが、全ての問題の始まり。

別に、htmlの要素のidに、コロンを含む記号は、問題なく指定できます。

シンプルなgetElementById("変なid名")も、特に問題なく動きます。

しかし。***ライブラリによっては、id名を元にする検索などで、思うように動いてくれない場合があります***。

今回は恐らく、bootstrapが依存するjQueryの問題ではないかと個人的に考えています。

(参考 : [https://www.buildinsider.net/web/jqueryref/036](https://www.buildinsider.net/web/jqueryref/036) )

# 終わりに
教訓はこの2つ。以後気をつけたい。

- idに記号を使う場合は要注意
    - ハイフンなど以外、入れないのが非常に好ましい
- 外部データの値をidに使う場合は要注意
    - 意図せず記号が入ってしまう場合がある

# 余談
該当のコードは、v-forの要素数オプションも使って、下のようにid名を工夫した。

```html:Detail.vue
<template>
    <div id="detail">
        <div
        class="accordion"
        :id="cardParent"
        >
            <Card
            v-for="(data,i) in jsonData"
            :key="data['@id']"
            :cardId="'card'+i"
            :title="data['title']"
            :parentId="cardParent"
            />
        </div>
    </div>
</template>
```