Onsen UI初心者がハマりがちなポイント（SPAについて）

[Onsen UI Advent Calendar 2016](http://qiita.com/advent-calendar/2016/onsenui) 19日目の記事です。

Onsen UI ではSPAという仕組みが採用されています。Onsen UIで複数ページから構成されるアプリを作成する場合、SPAについての理解は必須です。そこで今回はSPAについて解説します。

# SPAとは
SPAは、「Single Page Application」の略で、その名の通り単一のWebページによって構成されるアプリのことを指します。
アプリ起動時に全ページのリソース（HTMLファイル、CSSファイル、JavaScriptファイル）がロードされ、ページの切り替えはDOM操作によって行われるため、ストレスのない高速な画面遷移を実現できることが最大のメリットであると言えます。

# Onsen UIによるSPAの構築
Onsen UIでは、index.html の中に各ページが埋め込まれる構成となっています。
各ページは異なるHTMLファイルに分割して記述しても良いですし、index.htmlの中にまとめて記述しても良いです。
以下のコードは、どちらも同じことを行っています。

### ファイルを分割して記述
```html:index.html
<!DOCTYPE HTML>
<html>
<head>
  <!-- 各メタデータ要素は省略 -->
</head>
<body>
  <ons-page>
    <ons-toolbar>
      <div class="center" id="toolbar-title"></div>
    </ons-toolbar>
    <ons-tabbar position="auto">
      <ons-tab label="Tab 1" page="tab1.html" active>
      </ons-tab>
      <ons-tab label="Tab 2" page="tab2.html">
      </ons-tab>
    </ons-tabbar>
  </ons-page>
</body>
</html>
```

```html:tab1.html
<ons-page id="first-page">
  <p>
    This is the first page.
  </p>
</ons-page>
```

```html:tab2.html
<ons-page id="second-page">
  <p id="message">
    This is the second page.
  </p>
</ons-page>
```

### index.html内にまとめて記述
```html:index.html
<!DOCTYPE HTML>
<html>
<head>
  <!-- 各メタデータ要素は省略 -->
</head>
<body>
  <ons-page>
    <ons-toolbar>
      <div class="center" id="toolbar-title"></div>
    </ons-toolbar>
    <ons-tabbar position="auto">
      <ons-tab label="Tab 1" page="tab1.html" active>
      </ons-tab>
      <ons-tab label="Tab 2" page="tab2.html">
      </ons-tab>
    </ons-tabbar>
  </ons-page>

  <ons-template id="tab1.html">
    <ons-page id="first-page">
      <p>
        This is the first page.
      </p>
    </ons-page>
  </ons-template>

  <ons-template id="tab2.html">
    <ons-page id="second-page">
      <p id="message">
        This is the second page.
      </p>
    </ons-page>
  </ons-template>
</body>
</html>
```

`<ons-page>` がページを定義するタグ、 `<ons-template>` が仮想的なHTMLファイルを定義するタグです。



さて、ここから先がはじめてOnsen UIを使う人がハマりがちなポイントです。
Tab2が開いたときに、ページ内の要素をDOM操作したいといった場合、以下のように書くのは誤りです。

### 誤った例

```html:index.html
<!DOCTYPE HTML>
<html>
<head>
  <!-- 各メタデータ要素は省略 -->
</head>
<body>
  <ons-page>
    <ons-toolbar>
      <div class="center" id="toolbar-title"></div>
    </ons-toolbar>
    <ons-tabbar position="auto">
      <ons-tab label="Tab 1" page="tab1.html" active>
      </ons-tab>
      <ons-tab label="Tab 2" page="tab2.html">
      </ons-tab>
    </ons-tabbar>
  </ons-page>

  <ons-template id="tab1.html">
    <ons-page id="first-page">
      <p>
        This is the first page.
      </p>
    </ons-page>
  </ons-template>

  <ons-template id="tab2.html">
    <ons-page id="second-page">
      <p id="message">
        This is the second page.
      </p>
    </ons-page>
  </ons-template>

  <!-- tab2.htmlに対するDOM操作 -->
  <script>
    document.getElementById("message").textContent = "Tab2が開きました";
  </script>

</body>
</html>
```

`<body>` 内の一番下に `<script>` タグを書いているのに `getElementById()` で要素が取得できない！と思うかもしれません。

各ページに対してDOM操作やイベント登録などを行う場合は、ページが初期化されたタイミングで発生する `init` イベントを使うのが適切です。

### 正しい例

```html:index.html
<!DOCTYPE HTML>
<html>
<head>
  <!-- 各メタデータ要素は省略 -->
  <script>
    document.addEventListener("init", function(event) {
        var page = event.target;  // initイベントが発生したページ
        if(page.id === "second-page") {
            // tab2.htmlに対するDOM操作
            document.getElementById("message").textContent = "Tab2が開きました";
        }
    });
  </script>
</head>
<body>
  <ons-page>
    <ons-toolbar>
      <div class="center" id="toolbar-title"></div>
    </ons-toolbar>
    <ons-tabbar position="auto">
      <ons-tab label="Tab 1" page="tab1.html" active>
      </ons-tab>
      <ons-tab label="Tab 2" page="tab2.html">
      </ons-tab>
    </ons-tabbar>
  </ons-page>

  <ons-template id="tab1.html">
    <ons-page id="first-page">
      <p>
        This is the first page.
      </p>
    </ons-page>
  </ons-template>

  <ons-template id="tab2.html">
    <ons-page id="second-page">
      <p id="message">
        This is the second page.
      </p>
    </ons-page>
  </ons-template>
</body>
</html>
```

SPAとして構築されているために、ページが初期化されるタイミングが通常のWebアプリとは異なるのですね。
JavaScriptを書く場所がわからない！と悩んだ方の参考になれば幸いです。