無限スクロール(infinite scroll)を実現するいくつかの方法と、jScrollによる実装サンプル
一覧画面などで、リンクやボタンを押された場合に、モーダル画面で情報を表示するというニーズは結構あります。
自分で一から実装するとなると、結構大変だったりするので、既存の仕組みを利用するといいのですが、使い方に慣れていないと、少しの振る舞いを変更するのに、調べたりしないといけなかったりして、時間とっちゃうんですよね。。
なので、汎用的な仕組みを使えるようになっておくと便利です。
汎用的なモーダル画面の仕組みはいくつかありますが、私がいろいろと試行錯誤してたどり着いたのは、「BootstrapのModal」と「jQuery colorbox」の2つが使いやすいです。
(2018/11月現在)
この記事では、「BootstrapのModal」の使い方を、実践的に使うさまざまなTipsとともにご紹介します。
サンプルのソースは、なるべく汎用的に利用できるように記載していますので、使えるところはコピペで使ってください。
目次
BootstrapのModalの良いところ
colorboxを使うか、bootstrapのmodalを使うか、要は使いどころとか、案件の要件をどの程度満たすかだと思いますが、使ってみて、Bootstrapのmodalのいいところを述べたいと思います。
開くモーダル画面のデザインがしやすい。
colorboxは、モーダルの上下にタイトルやカスタムのボタンを作るのが難しいですが、Bootstrapの場合は比較的簡単に出来ます。
ただし、モーダルが開くときのエフェクトや振る舞いなどは、colorboxの方が細かい制御ができる印象があります。
ただ、BootstrapのModalでも、たいていのことはできるでしょう。
Bootstrapのmodalの組み込み方
はじめに、Bootstrap をダウンロードしてきます。
現在(2018/11月)は、ver4.1が最新版のようですが、今のところModalの使い方のサンプルなどがネット上に多いのはVer3なので、困ったときのためにも、Ver3.3.7を利用しました。
また、ネット上のいくつかのサンプルは、Bootstrapのデータ・バインドの仕組みを使っていて、簡単に記載できますが、どのIDとどの名称がつながっているのかが分かりにくく、動きを細かくカスタマイズしようとしたときに、手が止まってしまいがちなので、javascript(jQuery)を使って、制御するようなサンプルにしています。
なので、jQueryもダウンロードしておきます。
このサンプルでは、Ver3.3.1を利用しました。
用意はこの「Bootstrap 3」と「jQuery 3」だけなので、準備できたら、以下のソースを記載します。
親画面のソース
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <head> <title>Parent Page to Open Modal Window</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <script src="http://localhost/sample/jquery-3.3.1.min.js"></script> <script src="http://localhost/sample/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script> <link rel="stylesheet" href="http://localhost/sample/bootstrap-3.3.7-dist/css/bootstrap.min.css" /> <h1>Parent Page to Open Modal Window</h1> <script> $(function () { $("#myModal").on("show.bs.modal", function(e) { console.log('show.bs.modal'); }); }); function OnLinkClick(this_instance) { console.log('call OnLinkClick'); //選択されたレコードのvalue値を、モーダルのデータのロード先としてセット。 $('#selected_target').val(this_instance.getAttribute('value')); call_after_func(); $('#myModal').modal({ keyboard: true, backdrop: true }); return false; } function call_after_func() { console.log('call_after_func'); $(document).find("#modal-body").load($('#selected_target').val(), function(responseTxt, statusTxt, xhr){ if(statusTxt == "success") alert("External content loaded successfully!"); if(statusTxt == "error") alert("Error: " + xhr.status + ": " + xhr.statusText); }); } //モーダル側のcloseボタンが押されたときの処理 $(function () { $("#mdlClose").on("click", function(e) { console.log('call modal close'); console.dir($('#myModal')); $('#myModal').removeData('bs.modal'); $('body').removeClass('modal-open'); $('.modal-backdrop').remove(); $('#modal-body').empty(); $('#myModal').modal('hide'); }); }); </script> <input type="hidden" name="selected_target" id="selected_target" value=""> <a href='#responsive' onclick='OnLinkClick(this);' value='c1.html'>Launch Modal(C1)</a> <br /> <a href='#responsive' onclick='OnLinkClick(this);' value='c2.html'>Launch Modal(C2)</a> <!-- Default bootstrap modal example --> <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> <h4 class="modal-title" id="myModalLabel">Modal Contents</h4> </div> <div class="modal-body" id="modal-body"> ... </div> <div class="modal-footer"> <div class="text-center"> <button type="button" class="btn" data-dismiss="modal" id="mdlClose">Close</button> <button type="button" class="btn btn-primary" data-dismiss="modal" id="mdlChose">Chose</button> </div> </div> </div> </div> </div> </body> </html>
子画面のソース
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <head> <title>Child Page</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <h1>Child Page</h1> Child Page Contents </body> </html>
ソースの内容を解説
いくつかポイントとなるところを解説しておきます。
基本的には、Bootstrapのmodalは、親画面側に、子画面の入れ物も用意しておきます。
親画面の「id=”myModal”」となってるところが、Modalの枠になります。
この中に、「id=”modal-body”」というところがあると思いますが、ここに、リモートから読み込んだコンテンツをロードして、表示することになります。
親画面と子画面を用意するというよりは、「親画面と、子画面の枠を親側に用意しておき、ボタンがクリックされたら、その子画面の枠を表示したうえで、別途用意した子画面用コンテンツのページを非同期で読み込む」と考えるのがイメージしやすいと思います。
以下の解説で、いくつかオプションなどが出てきますが、基本手kにはver3もver4もほぼ一緒なので、「ここ」などを参照すると良いでしょう。
動きとしては、「Launch Modal」ボタンが押されたら、「OnLinkClick()」が呼ばれて、
$('#myModal').modal({ keyboard: true, backdrop: true });
で、モーダル画面を開きます。「keyboard」などは、オプションなので、渡さなくても大丈夫です。
「call_after_func()」の中では、ajaxで非同期にリモートページのコンテンツをGETし、「#modal-body」部分に読み込んでいます。
$(document).find("#modal-body").load($('#selected_target').val()
の部分ですね。
さらに、子画面の「Close」ボタンが押されたときの動きが、
$(function () { $("#mdlClose").on("click", function(e) { console.log('call modal close'); console.dir($('#myModal')); $('#myModal').removeData('bs.modal'); $('body').removeClass('modal-open'); $('.modal-backdrop').remove(); $('#modal-body').empty(); $('#myModal').modal('hide'); }); });
になります。
「removeData()」や「empty()」でコンテンツをきれいに掃除しているのがわかるでしょうか。これがないと、何度もモーダル表示を読んだ場合に、動きがおかしくなることがあります。
詰まったときやエラーの時に役立ちそうな情報
私が実装していた時に、困ったことで、解決したものを以下に記載しておきます。
モーダル画面を開いたら、親画面の上部にスクロールしてしまう
縦に長い画面などでモーダル画面を開いた場合、開いた位置はそのままでモーダルが開いてほしいですよね。
しかし、いちいちTopに移動してしまうことがあります。その場合、上記のサンプルのようにhrefの値を「#」ではなく「#responsive」にするとOKです。
ChromeなどのConsoleでエラーがでてしまう。
一応、エラーなく動くだけど、Chromeなどの開発者コンソールなどのデバッガで以下のエラーが出ることがあります。
Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience.
その場合、このページにも書いたように、子画面側のjavascriptの記述を見直すと良いです。
子画面のコンテンツ領域に縦のスクロールバーを表示させたい
子画面のコンテンツが縦に長い場合、スクロールバーを表示させたいですよね。その場合は、cssに以下の記述を追加すると良いです。
.modal-body { max-height: calc(100vh - 200px); overflow-y: auto; }