web creators×DeNA連動企画 Web制作現場で使える最新実用テクニック集

01 ソーシャルゲームに見るフロントエンド開発の最先端

【第2回】モーダル事例に見るHTML/CSS/JSの役割分担 その2

前回はモーダルウィンドウ自体および内容の表示/非表示を切り替える場合の、HTML、CSS、JavaScriptのそれぞれの役割について紹介した。JavaScriptで表示/非表示などの視覚的状態を制御するのではなく、視覚的状態はCSSで記述し、JavaScriptではclass名を制御するだけにとどめることで、表示効果の変更などにも柔軟に対応できる工夫だ。
今回はさらに一歩踏み込んで、モーダルウィンドウ内に異なる役割を持った複数のボタンが存在するケースについて解説していこう。

拡張性に乏しいJavaScriptコード

【1】複数のボタンを内包するモーダルウィンドウ

【1】複数のボタンを内包するモーダルウィンドウ

モーダルウィンドウの中に複数のボタンを表示し、各ボタンには異なる役割(機能)が割り当てられているとする【1】。
各役割はJavaScriptの関数として用意するとした場合、JavaScript初級者なら【2】のようなJavaScriptコードを考えるのではないだろうか。

JavaScript
var button1 = document.getElementById('button1'); var button2 = document.getElementById('button2'); var button3 = document.getElementById('button3'); button1.addEventListener('click', func1, false); button2.addEventListener('click', func2, false); button3.addEventListener('click', func3, false);

【2】id属性を利用して複数のボタンにそれぞれ異なる機能を割り当てるコード例

このコードでは、getElementByIdによってユニークなid属性を持つ要素を特定することで、個別に関数呼び出しを行っている。
ボタンが3個程度であれば、これでもよい。しかし、あらかじめ多くのボタンを用意しなければならない場合、さらにボタンが追加されることが想定されるようなケースでは、このようなコードは汎用性・拡張性に乏しいといえる。
前回同様、HTML、CSS、JavaScriptの各役割を考えて、できることは互いに任せることが重要である。

複数のボタンごとに呼び出す関数を自動セット

まずは【3】のようなHTMLがあるとしよう。

HTML
<button data-role="confirm">確認</button> <button data-role="submit">購入</button> <button data-role="cancel">キャンセル</button> <button data-role="close">閉じる</button> <button data-role="back">戻る</button>

【3】独自データ属性を使って、各button要素に異なる役目を定義したHTMLコード例

button要素には、HTML5で利用できるようになった独自データ属性「data-」を使って、JavaScript側で関数として用意するメソッドに対応した属性値を与えている。
これに対して、JavaScriptでは【4】のようなコードを用意する。

JavaScript
var elements = document.querySelectorAll('[data-role]'); for (var i = 0, len = elements.length; i < len; i++) { var role = elements[i].getAttribute('data-role'); //Androidではdatasetが使えないため、getAttributeを使用 elements[i].addEventListener('click', methods[role], false); } var methods = { confirm: function(){}, submit: function(){}, cancel: function(){}, close: function(){}, back: function(){} };

【4】独自データ属性を利用して各button要素に固有の機能を割り当てるJavaScriptコード例

このようにボタンに対応する関数の呼び出しをHTMLコードの属性値で結びつけることで、固有のボタンとそのメソッド名を自動的に取得し、
clickイベントで各自関数を呼び出すように自動化している。
これにより、ボタンの増減、変更などがあった場合も最小限のコード修正で対応することが可能だ。

delegateを利用した複数オブジェクトの制御

では、複数のボタンとそれを表示する複数のモーダルウィンドウを表示しなければならないケースはどうだろう。
たとえば、そういったモーダルウィンドウがふたつの場合、【5】のようなJavaScriptコードが思いつく。

JavaScript
var button1 = document.getElementById('button1'); var button2 = document.getElementById('button2'); var modal1 = document.getElementById('modal1'); var modal2 = document.getElementById('modal2'); button1.addEventListener('click', function() { modal1.style.display = 'block'; }, false); button2.addEventListener('click', function() { modal2.style.display = 'block'; }, false);

【5】id属性を利用して複数のモーダルウィンドウ(ボタンを含む)を表示するJavaScriptコード例

各モーダルウィンドウとボタンをユニークなid属性値で特定して表示/非表示を切り替えているが、もっと大量の要素を制御する必要がある場合は非効率的だ。
前述のようにループ処理によって、個別要素を配列に格納して処理するというのもスマートではない。
そこで利用したいのがclass名とdelegateによる処理だ。
まず、【6】のようなHTMLとCSSがあるとしよう。

HTML
<div id="container"> <div class="modal1">モーダルウィンドウ1</div> <div class="modal2">モーダルウィンドウ2</div> </div> <button data-target-scene="scene-modal1">ボタン1</button> <button data-target-scene="scene-modal2">ボタン2</button>
CSS
.modal1, .modal2 {display: none;} .scene-modal1 modal1, .scene-modal2 .modal2 {display: block;}

【6】複数のモーダルウィンドウ(ボタン含む)を表示するコード例。シーンを定義してCSSで表示/非表示を切り替えることを想定している。

これに対して、JavaScript側では【7】のようなコードを用意する(jQueryを利用している)。

JavaScript
var container = $('#container'); $(document.body).delegate('[data-target-scene]', 'click', function() { container.addClass($(this).attr('data-target-scene'); });

【7】定義したシーン(class名)でモーダルウィンドウの表示/非表示を切り替えるJavaScriptコード例

このJavaScriptコードでは、delegeteによって各要素で発生したclickイベントをbody要素に委譲したうえで、各要素に設定した独自データ属性「data-target-scene」の値を利用することで表示を切り替えている。
id属性で個別に記述するのではなく、またループ処理を回す必要もなく、たったこれだけのコードで大量のモーダルウィンドウとボタンを制御できてしまう。

コラム delegateについて

delegateはイベントハンドリング関数のひとつで、内包するある子要素に発生したイベントを親要素に委譲して、親要素がイベントの発生を取得できるようにする。
このとき、イベントがDOMツリーを祖先要素に向かって伝播していくことになり、これをイベントバブリングと呼んでいる。
つまり、このバブリングの仕組みを利用して、実際にイベントを補足したい要素の祖先要素でイベントを捕捉して、なんらかの関数を呼び出すといった仕組みを実装できるのがdelegateである。

まとめ

2回に渡ってフロントエンド実装現場での具体的なコード事例を掲載してきたが、これらは初級エンジニアから中級エンジニアになるためには必須と思われるコーディング上の工夫だ。
スマートフォン向けのコンテンツ開発を考えた場合、実際にはAndroidの実装状況により、また利用できないAPIなどもあり、PC向けとは異なる制約もあるが、今後OSの進化とともにより汎用性が高く効率的なコーディングが可能となるはずだ。
そういった状況になることを踏まえて、現時点からHTML、CSS、JavaScriptの各役割分担を考慮したコード設計に慣れておくことが大切である。

【著者紹介】

草間 正則(フロントエンドエンジニア)

数社でHTML/CSS/JavaScriptコーディング、およびUI設計を担当した後、株式会社ディー・エヌ・エーに入社。
フロントエンドエンジニアとしてソーシャルゲームサイトのコーティングを担当。ひとつのdivタグとCSSでリラ◯クマを描き、「変態」タグを付けられた。

株式会社ディー・エヌ・エー

1999年に設立され、同年オークションサイト「ビッダーズ」をスタート。2001年には、同サイトをオークション&ショッピングサイトにリニューアルし、本格的なEコマース事業へと乗り出す。2004年には、携帯電話向けのオークションサイト「モバオク」、ショッピングサイト「ポケットビッダーズ」などを開始。2005年に東京証券取引所マザーズにて上場を果たす。2006年には携帯電話向けゲームをベースとしたSNS「モバゲータウン」(現:Mobage)を開始。ユーザから爆発的な人気を博し、2007年には東京証券取引所市場一部に上場。その後、PC向けサービスやスマートフォン向けサービスなど各種サービスをマルチデバイスで展開する一方、アジアなど海外でもサービスを展開。2012年4月、本社を東京渋谷のヒカリエに移転した。

URL https://dena.jp/
関連記事 「こんなオフィスで働きたい!」