
こんにちは、CTOの奥田です。
先日イチロー選手が引退されました。
小学生の頃、近所の友達とグリーンスタジアムにオリックスの試合を何度か観に行きました。
彼の素晴らしいプレーとその肩に書かれた「がんばろう神戸」の文字は私達に勇気をくれました。
いつまでも私のヒーローはイチロー選手です。
長い現役生活、本当にお疲れ様でした。
さて、今回はPure JavaScriptで書かれたシンプルなモーダルを実装できるプラグイン「Tingle.js」をご紹介致します。
Table of contents
Tingle.jsとは
Tingleは Pure JavaScriptで書かれたシンプルなモーダルプラグインです。
軽量かつjQueryなど他のライブラリとの依存関係は必要なく、簡単に実装できます。
以下に公式サイトに載っている説明の箇条書きを載せておきます。
- 依存関係は必要ありません
- CSSで完全にカスタマイズが可能です
- CSSトランジションで動作します
- 単純なAPI
- ダウンロードする追加ファイルはありません
- UXを念頭に置いて作成しています
インストール
まずはインストールしましょう。
bower install tingle --save
npm install tingle.js --save
<script src="tingle.min.js"></script>
Tingle.jsはCSS, Less, Sassなど様々な方法でカスタマイズが可能です。
<link rel="stylesheet" href="tingle.min.css">
使い方
使い方は簡単です。
jQueryのプラグインほど簡単ではありませんが、拡張性が高く作られていてJSが書ける人にはこちらの方が柔軟にカスタマイズが可能かと思います。
まずは公式サイトに載っているサンプルをそのまま貼り付けてみた以下のデモを見てみてください。
tingleは new tingle.modal()でインスタンスを生成することで使用可能となります。
var modal = new tingle.modal();
以下のオプションを指定可能です。
| 名 | タイプ | 説明 |
|---|---|---|
| footer | boolean | フッターを表示するかどうか |
| stickyFooter | boolean | フッターを固定する場合はtrueを設定します |
| closeMethods | array | 利用可能なcloseメソッド(オーバーレイ、ボタン、エスケープ) |
| onOpen | function | tingleが開いているときに実行するコールバック(遷移終了後) |
| onClose | function | tingleが閉じられたときに実行されるコールバック |
| beforeOpen | function | モーダルを開く前に実行するコールバック |
| beforeClose | function | モーダルを閉じる前に実行するためのコールバック(モーダルを閉じるにはtrueを返さなければなりません) |
| cssClass | array | Tingleコンテナに追加されるカスタムCSSクラス |
| closeLabel | string | 閉じるボタンに表示されるラベル(モバイル版) |
また、以下のメソッドを使用してmodalをコントロールできます。
| Name | Description |
|---|---|
| open() | モーダルを開きます (.tingle-enabledがbodyに追加されます) |
| close() | モーダルを閉じ、存在する場合はコールバックを実行します |
| setContent(content) | モーダルコンテンツを設定する |
| getContent() | モーダルコンテンツを取得する |
| setFooterContent(content) | フッターコンテンツを設定する |
| getFooterContent() | フッターコンテンツを取得する |
| addFooterBtn(label, cssClass, callback) | フッターにボタンを追加します(スタイルと配置にはcssClassを使用します) |
| checkOverflow() | モーダルの位置を変更できるようにします(非同期コンテンツで役立ちます) |
| isOverflow() | モーダルの高さがビューポートの高さよりも大きい場合はtrueを返します |
| destroy() | モーダルを削除します(DOMから外してイベントをアンバインドする) |
カスタマイズ方法
このままだとかなりポップな感じの印象ですがTingle.jsは簡単に見た目をカスタマイズすることができます。
以下のデモをご確認ください。
記述したスタイルは以下です。
このように意外と簡単にカスタマイズが可能です。
/**
* tingle modal modified
**/
.tingle-modal {
padding-top: 40px;
padding-bottom: 40px;
}
.tingle-modal-box {
width: 850px;
max-width: 90%;
border-radius:0 ;
}
.tingle-modal-box__content{
padding: 0;
}
.tingle-modal__close{
height: 45px;
width: 48px;
outline:none !important;
background-color: rgba(0,0,0,.8);
}
@media(max-width: 540px){
.tingle-modal {
padding: 20px 15px;
}
.tingle-modal-box {
max-width: 100%;
}
.tingle-modal__close{
font-size: 1.6rem;
left: auto;
right: 0px;
line-height: 1.6rem;
}
.tingle-modal__closeIcon{
font-size: 1.2rem;
margin-right: 0;
}
.tingle-modal__closeLabel{
display: none;
}
}
@media(min-width: 541px){
.tingle-modal__close{
font-size: 1.2rem;
top: 20px;
right: 20px;
}
.tingle-modal__closeIcon{
font-size: 1.2rem;
margin-right: 0;
}
}
また、要素をdisplay:noneにした要素で囲み、その中身をsetContentすることで汎用性の高いモーダルを実装することができます。
<div class="d-none">
<div id="modal__content">
ここにコンテンツを配置
</div>
</div>
// data-modal属性を持つトリガーボタンを取得
var modalTrigger = document.querySelectorAll('[data-modal]');
if(modalTrigger.length > 0){
for (let i = 0; i < modalTrigger.length; i++) {
var el = modalTrigger[i];
//イベントをバインド
_addModalEvent(el);
}
}
function _addModalEvent(el){
el.addEventListener('click',function(event){
event.preventDefault();
// data-modalに指定したIDの要素を取得
var target = el.getAttribute('data-modal');
// モーダルのコンテンツを取得
var modalContent = document.querySelector(target);
if(modalContent){
// コンテンツをセット
modal.setContent(modalContent.innerHTML);
// モーダルオープン
modal.open();
}
})
}
iframeの表示
先程のサンプルでiframeを非表示要素として配置してしまうとURLの末尾にautoplay=1をつけている場合、表示はされていませんが動画自体が再生されてしまいます。
そんな時は動画のURLを取得してiframe要素を毎回生成してしまいましょう。
<a href="" class="btn btn-primary btn-lg" data-modal data-src="Youtube URL">modal open</a>
var modalContent = "";
// instanciate new modal
var modal = new tingle.modal({
beforeClose: function() {
// モダルの要素内を削除
modal.setContent("");
return true;
}
});
var modalTrigger = document.querySelectorAll('[data-modal]');
if(modalTrigger.length > 0){
for (let i = 0; i < modalTrigger.length; i++) {
var el = modalTrigger[i];
_addModalEvent(el);
}
}
function _addModalEvent(el){
el.addEventListener('click',function(event){
// 通常のクリックイベントをキャンセル
event.preventDefault();
var target = el.getAttribute('data-modal');
var src = el.getAttribute('data-src');
if(src!=""){
// .iframe-content要素を生成
var iframeContent = document.createElement('div');
iframeContent.setAttribute("class","iframe-content");
// iframe要素を生成
var iframe = document.createElement('iframe');
// 必要な属性を付与する
iframe.setAttribute("allow","autoplay; encrypted-media");
iframe.setAttribute("allowfullscreen","");
iframe.setAttribute("frameborder","0");
// src属性をセット
iframe.setAttribute("src",src);
// .iframe-content要素に追加
iframeContent.appendChild(iframe);
// modalにセット
modal.setContent(iframeContent);
// modalをopen
modal.open();
}
})
}
フッターボタンのコールバック
フッターにボタンを追加して何かイベントを実行したい場合はaddFooterBtnを使います。
また、closeMethodsを空にすることで他のボタンで閉じる動作をなくすことができます。
押すたびにどんどんボタンが増えていくのでsetFooterContent("")を忘れずに。
var modalContent = "";
// instanciate new modal
var modal = new tingle.modal({
footer:true,
closeMethods:[],
beforeClose: function() {
return true; // close the modal
}
});
// data-modal属性を持つトリガーボタンを取得
var modalTrigger = document.querySelectorAll('[data-modal]');
if(modalTrigger.length > 0){
for (let i = 0; i < modalTrigger.length; i++) {
var el = modalTrigger[i];
//イベントをバインド
_addModalEvent(el);
}
}
function _addModalEvent(el){
el.addEventListener('click',function(event){
event.preventDefault();
// data-modalに指定したIDの要素を取得
var target = el.getAttribute('data-modal');
// モーダルのコンテンツを取得
var modalContent = document.querySelector(target);
if(modalContent){
// コンテンツをセット
modal.setContent("");
modal.setContent(modalContent.innerHTML);
modal.setFooterContent("");
modal.addFooterBtn('閉じる', 'tingle-btn tingle-btn--primary', function() {
if(window.confirm('モーダルを閉じます。よろしいですか?')){
modal.close();
}
});
// モーダルオープン
modal.open();
}
})
}
さいごに
今回はかなり便利なモーダルを実装可能なTingle.jsをご紹介させていただきました。
今やモーダルはCSSで実装できてしまう時代ではありますが、様々なイベントの実行などを柔軟に行うには、やはりJavaScriptの力なしではできません。
皆さんもぜひ使ってみてください。