lambda consulting

Foundation Reveal using Ajax

July 01, 2016

ZURB Foundation

ZURB Foundationは、CSSのフレームワーク。Bootstrap が有名だが、Foundationだってぜんぜん負けてない。
今回、書いておきたいのは、Foundation の1機能である RevealAjax を使った場合にはまってしまったことについて。

Reveal とは、JQueryのmodal pluginを ZURB風にしたもの。( modalというのは、子画面みないなものだが、それに対して適切に応答しないと、親画面へ戻れないというようなUIのこと)

そのmodalで表示したい子画面内のコンテンツについては、大きく次の2パターンがある。

  1. 親画面が表示される段階で既に準備されている。クリックなどのユーザアクションにより、元々持っているいるものをmodal表示するだけ。(親画面へのリクエストに対するレスポンスに既に含まれているので、新たなサーバとの通信は発生しない)

  2. 親画面が表示される段階では、まだ準備されていない。クリックなどのユーザアクションにより、条件に応じたコンテンツをサーバより取得して、modal表示する。(modalを表示するタイミングでサーバとの間にAjax通信が発生)

Revealを使うと、1. は、非常に簡単にできる。(JavaScriptは、一切書かなくてもいい)

<!-- modalとなる部分(子画面の中身) : 最初は表示されていない -->
<div class="reveal" id="modal-1" data-reveal>
  <h1>Modal のサンプル</h1>
  <p>このdiv内は、最初は表示されない。「Modal を表示する」がクリックされると表示される。</p>
  <button class="close-button" data-close aria-label="Close modal" type="button">
    <span aria-hidden="true">&times;</span>
  </button>
</div>

<!-- modal表示のトリガーとなるリンク -->
<a data-open="modal-1">Modal を表示する</a>

これだけで、modalが実現できる。
data-open 属性がついているリンクがクリックされると、そこに指定されているidのreveal要素がmodalとして表示される。
尚、div.reveal の中のbutton要素は、一番大袈裟に見えるが、modalを閉じるための X ボタンなだけ。

さて、本題は、2. のAjaxを使う方。

<div class="reveal" id="modal-2" data-reveal>
</div>

<a href="#" onClick="showModal">Modal を表示する</a>

親画面が表示される段階では、未だ中身がないので、柄だけ置いておく。(modalなので、置いておく位置は関係ない)
子画面の中身を取得して、modal表示するのは、次のjavascriptになる。

function showModal() {
  var $modal = $('#modal-2');

  $.ajax('/url_of_api')
    .done(function(resp){
      $modal.html(resp.html).foundation('open');
  });
}

(エラー処理もないし、かなり簡略化しているが、)サーバからAjaxで取得した resp をreveal要素(#modal-2)に入れ込んだ上で、foundation(‘open’) というmodalを表示させるためのメソッドを呼ぶだけである。これでちゃんと取得した内容でmodalが表示される。

で、最初は気づかなかったのだが、親画面がスクロールする程に長くなった際に、問題が発覚した。

親画面が初期表示から、いくらかでも下方にスクロールしている状態で、modal表示がされると、modal(子画面)は表示されるのだが、それと同時に裏側にある親画面のスクロールがトップに戻ってしまうのである。

で、modalを消して、親画面に戻るとスクロールトップの状態のままなので、(おそらく)ユーザは、再びスクロールをさせることになる。素直なユーザは、「こういう仕組みなのだ」と思い、特に不具合とは思わないかもしれないが、もし、親画面が相当量のスクロールを前提とするようなデザインなら、相当イライラするだろう。

Ajaxでなく、1. のように静的にmodalを表示する場合だと、親画面のスクロールは維持されるので、原因がよくわからなかったのだが、軽くデバッグでjavascriptを流して見ていると、なにやらevent周りで、処理が飛び回っているように見えた。

Ajaxでない場合には大丈夫なのが腑に落ちないが、バブリング対策として、javascriptのコードを次のように変更すると、問題が解消した。

function showModal(e) {  // eventを取る
  var $modal = $('#modal-2');

  e.preventDefault()     // イベントキャンセル
  e.stopPropagation()    // バブリング停止

  $.ajax('/url_of_api')
    .done(function(resp){
      $modal.html(resp.html).foundation('open');
  });
}