[Javascript] alertやconfirmを見栄え良くデザインできる「Modal-JS」ライブラリ

2019年6月19日

Javascript テクノロジー プログラミング

MicrosoftのIEブラウザが嫌いなエンジニアは多いのですが、まだまだ世の中で使われているWEBブラウザの上位ランクされていることには間違いはありません。 これはwindowsが低リテラシの人に使われいる以上、どうしようも無い事なのかもしれませんが、ブラウザの古くからある基本機能のalert関数も、ブラウザによってデザインや表示文言仕様は様々な状態になっています。 WEB開発において、UIデザインを重んじてきている昨今では、こうしたalert表示ではなく、独自のモーダル表示で行うようになってきている傾向があります。 今回はそんなモーダル機能を簡単に実現できるように、ライブラリを作ってみました。 (自分が仕事で使えるように作っただけですが・・・)

ソースコード

<!doctype html> <html> <head> <meta charset="utf-8"> <title>Modal.js</title> <link rel="stylesheet" href="modal.css"> <script src="modal.js"></script> </head> <body> <h1>Modal.JS</h1> <button class="modal-alert" type="button">Modal-Alert</button> <button class="modal-confirm" type="button">Modal-Confirm</button> <script> var btn1 = document.querySelector(".modal-alert"); btn1.onclick = function(){ new $$modal({ title : "アラート", close : { html : "", size : 30 }, message : "このウィンドウを閉じるには、「×」ボタンを押すか、OKをクリックしてください。", button : [ { mode : "close", text : "OK" } ], bgClick : "none" }); }; var btn2 = document.querySelector(".modal-confirm"); btn2.onclick = function(){ new $$modal({ title : "確認ウィンドウ", close : { html : "", size : 16 }, message : "このウィンドウを閉じるには、「×」ボタンを押すか、OKをクリックしてください。", button : [ { mode : "close", text:"キャンセル", click:function(){console.log("cancel");} }, { mode : "close", text:"OK" } ], bgClick : "close" }); }; </script> </body> </html> *, *:before, *:after { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box; } .modal{ position:absolute; top:0; left:0; width:100%; height:100%; background-color:rgba(0,0,0,0.5); } .modal * { font-size:12px; color:#666; } .modal-base{ position:relative; width:300px; background-color:white; border-radius:4px; padding:10px; } .modal-close{ position:absolute; top:10px; right:10px; } .modal-close > .modal-close-icon{ position:relative; width:100%; height:100%; margin:0; padding:0; cursor:pointer; } .modal-close > .modal-close-icon:empty:hover{ opacity:0.5; } .modal-close > .modal-close-icon:empty:before{ content:""; position:absolute; top:calc(50% - 1px); left:10%; width:80%; height:1px; border-bottom:1px solid #666; transform:rotate(45deg); transform-origin:center; } .modal-close > .modal-close-icon:empty:after{ content:""; position:absolute; top:calc(50% - 1px); left:10%; width:80%; height:1px; border-bottom:1px solid #666; transform:rotate(-45deg); transform-origin:center; } .modal-title{ width:100%; font-size:14px; text-align:center; } .modal-message{ width:100%; min-height:100px; font-size:12px; padding:10px; display:table-cell; vertical-align:middle; } .modal-button-area{ text-align:center; } button.modal-button{ display:inline; min-width:80px; height:30px; font-size:12px; margin:8px; padding:0 8px; border-radius:4px; box-shadow:0px 0px 4px rgba(0,0,0,0.3); cursor:pointer; } button.modal-button:hover{ opacity:0.5; } /* Animation */ .modal{ opacity:0; } .modal, .modal .modal-base{ animation-duration: 0.3s; animation-fill-mode: forwards; } .modal[data-view="1"]{ /* animation: anim-modal-view 0.3s linear forwards; */ animation-name: anim-modal-view; animation-timing-function: linear; } @keyframes anim-modal-view{ 0%{ opacity:0; } 100%{ opacity:1; } } .modal[data-view="1"] .modal-base{ /* animation: anim-modal-slidein 0.3s ease-out forwards; */ animation-name: anim-modal-slidein; animation-timing-function: ease-out; } @keyframes anim-modal-slidein{ 0%{ transform:translateY(-150px); } 100%{ transform:translateY(0px); } } .modal[data-view="0"]{ /* nimation: anim-modal-close 0.3s linear forwards; */ animation-name: anim-modal-close; animation-timing-function: linear; } @keyframes anim-modal-close{ 0%{ opacity:1; } 100%{ opacity:0; } } .modal[data-view="0"] .modal-base{ /* animation: anim-modal-slideout 0.3s ease-out forwards; */ animation-name: anim-modal-slideout; animation-timing-function: ease-out; } @keyframes anim-modal-slideout{ 0%{ transform:translateY(0px); } 100%{ transform:translateY(-150px); } } ;$$modal = (function(){ /** * ModalJS * == * [Summary] * alertやconfirm処理をデザイン対応する時のモーダル表示処理。 * 背景に黒半透明elmを置くことで、画面のelementにアタッチできなくさせる。 * [Howto] * * */ var $$event = function(target, mode, func){ if (typeof target.addEventListener !== "undefined"){target.addEventListener(mode, func, false);} else if(typeof target.attachEvent !== "undefined"){target.attachEvent('on' + mode, function(){func.call(target , window.event)});} }; var $$urlinfo = function(uri){ uri = (uri) ? uri : location.href; var urls_hash = uri.split("#"); var urls_query = urls_hash[0].split("?"); var sp = urls_query[0].split("/"); var data={ uri : uri, url : sp.join("/"), dir : sp.slice(0 , sp.length-1).join("/") +"/", file : sp.pop(), domain : sp[2], protocol : sp[0].replace(":",""), query : (urls_query[1])?(function(urls_query){ var data={}; var sp = urls_query.split("&"); for(var i=0;i<sp .length;i++){ var kv = sp[i].split("="); if(!kv[0]){continue} data[kv[0]]=kv[1]; } return data; })(urls_query[1]) : [] , hash : (urls_hash[1]) ? urls_hash[1] : "" }; return data; }; var $$init = function(){ switch(document.readyState){ case "complete" : new $$; break; case "interactive" : $$event(window , "DOMContentLoaded" , (function(e){new $$}).bind(this)); break; default : $$event(window , "load" , (function(e){new $$}).bind(this)); break; } }; var $$ = function(options){ this.reflectOptions(options); this.view(); }; $$.prototype.options = { size : { width : "300px", height: "auto" }, close : { html : "", size : 16 }, title : "Title", message : "Message", button : [ { mode:"close", text:"Click" } ], bgClick : "close" // [ "close" , "none" ] }; $$.prototype.reflectOptions = function(options){ if(!options){return;} for(var i in options){ this.options[i] = options[i]; } }; $$.prototype.view = function(){ if(!document.body){return} // BG var bg = document.createElement("div"); bg.className = "modal"; if(this.options.bgClick === "close"){ $$event(bg , "click" , (function(e){this.clickBg(e)}).bind(this)); } document.body.appendChild(bg); // Base var base = document.createElement("div"); base.className = "modal-base"; base.style.setProperty("width" , this.options.size.width , ""); base.style.setProperty("height" , this.options.size.height , ""); bg.appendChild(base); // Close var close = document.createElement("div"); close.className = "modal-close"; var closeHTML = ""; closeHTML = "<div class='modal-close-icon'>"; closeHTML += this.options.close.html; closeHTML += "</div>"; close.innerHTML = closeHTML; close.style.setProperty("width" , this.options.close.size + "px" , ""); close.style.setProperty("height" , this.options.close.size + "px" , ""); close.onclick = (function(e){this.close(e);}).bind(this); base.appendChild(close); // Title var title = document.createElement("div"); title.className = "modal-title"; title.innerHTML = this.options.title; base.appendChild(title); // Message var message = document.createElement("div"); message.className = "modal-message"; message.innerHTML = this.options.message; base.appendChild(message); // Buttons var btnArea = document.createElement("div"); btnArea.className = "modal-button-area"; base.appendChild(btnArea); var btn = []; for(var i in this.options.button){ btn[i] = document.createElement("button"); btn[i].className = "modal-button"; btn[i].innerHTML = this.options.button[i].text; if(this.options.button[i].click){ btn[i].onclick = this.options.button[i].click; $$event(btn[i] , "click" , (function(e){this.options.button[i].click}).bind(this)); } if(this.options.button[i].mode === "close"){ $$event(btn[i] , "click" , (function(e){this.close(e);}).bind(this)); } btnArea.appendChild(btn[i]); } // position base.style.setProperty("top" , (window.innerHeight / 2) - (base.offsetHeight / 2) + "px" , ""); base.style.setProperty("left" , (window.innerWidth / 2) - (base.offsetWidth / 2) + "px" , ""); // animation bg.setAttribute("data-view","1"); }; $$.prototype.close = function(){ var modal = document.querySelector(".modal"); if(!modal){return;} modal.setAttribute("data-view" , "0"); setTimeout(function(){ var modal = document.querySelector(".modal"); modal.parentNode.removeChild(modal); },500); }; $$.prototype.clickBg = function(e){ var target = e.target; if(!target.matches(".modal")){return;} this.close(); }; return $$; })();

使い方

new $$modal({ title : "アラート", close : { html : "", size : 30 }, message : "このウィンドウを閉じるには、「×」ボタンを押すか、OKをクリックしてください。", button : [ { mode : "close", text : "OK", click: function(){console.log("cancel");} } ], bgClick : "none" }); ボタンを押したタイミングや、リンククリックのタイミングで、上記のように"$$modal"をインスタンス化して、送り値としてオプションを設定すれば、モーダルダイアログが機能するようになります。 注意点としては、"button"設定は、1つだけでも、必ず配列にしてください。 サンプルプログラムを見るとわかるかもしれませんが、button設定に"click"という設定をすると、そのボタンを押した時に、任意のプログラムを実行することができるので、機能連動させることができます。 buttonの"mode"は、デフォルトでは「close」となっていて、押した後モーダルを閉じるようにしていますが、閉じない場合は"none"にしてください。 "bgClick"というのは、モーダルが表示されている時に背景が黒の半透明になっていますが、そこをクリックした場合にモーダルを閉じるかどうかのモード設定になっています。 デフォルトは"none"で、何もしないのですが、枠外クリックでモーダルを閉じたい場合は、"close"としてください。 あとは、記入できる文字列は全てinnerHTMLで表示されるので、タグ記述が可能になります。

サンプル

See the Pen modal-js by YugetaKoji (@geta1972) on CodePen.

改善について

今回のモーダルは、レスポンシブを考慮していません。 cssファイルに画面サイズに応じたサイズや座標などを記述しなければいけません。 また、スクロール対応もしていないので、modalクラスをpositiopn:fixedにした方がいいかもしれませんね。 とりあえず、今の作業はPCブラウザのみだったので、このライブラリでver1.0にしておこうと思います。

人気の投稿

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。

ブログ アーカイブ