Javascriptでのダイアログは昔から「alert関数」をよく使っていますが、最近のサービスでは、alertを使っていないものが多いなあと感じていたところ、エンジニア友達から、「どうやらalert関数は使わない方がいい」との話を聞いたので、その理由と対応できるプログラム構築をサクっとしてみました。
ちなみに、デバッグなどの際にも非常に簡単で効率的にセットできるため、これまで多様化してたんですが、
そのほかにも、submit時に使う「confirm」や、何かの入力を簡易的に行う「prompt」もalertと同じく使わない方が良さそうですね。
alertを使うデメリット
alert , confirm , prompt , は、ブラウザのコントロール全てを一旦停止してしまいます。
alertが出ている間は、ブラウザのタブの切り替えすら行う事ができないので、昔からブラクラ(ブラウザクラッシュ)手段でもよく使われてきました。
無限ループを起こすfor文内にalertを書き込むと、アプリを強制終了する以外に停止させる方法がなくなってしまいます。
ただし、最近のブラウザバージョンでは、この無限ループalertを防止する為に、同じalert表示が繰り返されると、alert表示を止めるチェックボックスが現れるようになっています。
ただし、chrome extensionなどでwindow.openなどのブラクラは今だに存在するので、要注意ですが・・・
ブラクラの他にも、そもそもブラウザの挙動を全て停止して他の作業ができなくなる点から、alertなどの処理を使うのは望ましく無いようですね。
PCは全てにおいてマルチタスクが原則だと考えるUI/UXを考慮すると、確かにalert処理は非効率である事がわかります。
では、どうすればいいか?
これの答えは簡単で、ページ内のエレメント処理を使って行えばいいだけです。
ようするに、ダイアログを表に表示する処理さえできれば、問題なくalertの用途は充します。
また、同一タブで表示されているものをクリックできなくさせる効果も一番上のレイヤーに画面を覆い尽くす1枚のエレメントを設置する事で、対応可能です。
ソースコード
;(function(){
/**
* Alerter.js
* デフォルトalert関数を使わずに柔軟な表示が出来るツール
* [usage]
* $$ALERTER(%message[string] , %action[function]);
*/
var $$ = function(msg,action){
$$.prototype.setStyle();
$$.prototype.setBG();
$$.prototype.setDialog(msg,action);
if(typeof $$.prototype.scrollFlg === "undefined"){
$$LIB.prototype.setEvent(window,"scroll",$$.prototype.setDialogPosition);
}
$$.prototype.scrollFlg = true;
};
$$.prototype.setStyle = function(){
if(document.getElementById("alerter_css")!==null){return;}
var style = document.createElement("style");
style.type = "text/css";
style.id = "alerter_css";
var css = "";
css += "head,body{width:auto;height:auto;}"+"\n";
css += "body{position:relative;}"+"\n";
css += "#alerter{"+"\n";
css += " position:absolute;"+"\n";
css += " display:inline-block;"+"\n";
css += " background-color:rgba(0,0,0,0.5);"+"\n";
css += " width:100%;"+"\n";
css += " height:100%;"+"\n";
css += " top:0;"+"\n";
css += " left:0;"+"\n";
css += " mergin:0;"+"\n";
css += " padding:8px;"+"\n";
css += " z-index:10000;"+"\n";
css += "}"+"\n";
css += "#alerter > .alerter_dialog{"+"\n";
css += " position:absolute;";
css += " display:block;";
css += " width:200px;";
css += " background-color:#ccc;";
css += " margin:0 auto;";
css += " padding:8px;";
css += " border:0;";
css += " border-radius:4px;";
css += "}"+"\n";
css += "#alerter > .alerter_dialog > .alerter_text{"+"\n";
css += " display:block;";
css += " min-height:40px;";
css += " line-height:20px;";
css += " padding:0;";
css += "}"+"\n";
css += "#alerter > .alerter_dialog > .alerter_btn{"+"\n";
css += " display:inline-block;";
css += " right:0;";
css += " float:right;";
css += "}"+"\n";
css += "#alerter > .alerter_dialog > .alerter_clear{"+"\n";
css += " clear:both;";
css += "}"+"\n";
style.innerHTML = css;
document.getElementsByTagName("head")[0].appendChild(style);
};
$$.prototype.clickCheckBG = function(event){
var elm = event.target;
if(elm.id !== "alerter"){return;}
$$.prototype.delBG();
};
$$.prototype.setBG = function(){
$$.prototype.delBG();
var body = document.body;
var div = document.createElement("div");
div.id = "alerter";
// div.onclick = $$.prototype.clickCheckBG;
document.body.appendChild(div);
};
$$.prototype.delBG = function(){
var alerter = document.getElementById("alerter");
if(alerter !== null){
alerter.parentNode.removeChild(alerter);
}
};
$$.prototype.setDialog = function(msg,action){
var alerter = document.getElementById("alerter");
if(alerter === null){return;}
var div = document.createElement("div");
div.className = "alerter_dialog";
var txt = document.createElement("div");
txt.className = "alerter_text";
txt.innerHTML = msg;
div.appendChild(txt);
var btn = document.createElement("button");
btn.className = "alerter_btn";
btn.type = "button";
btn.innerHTML = "OK";
div.appendChild(btn);
var clr = document.createElement("div");
clr.className = "alerter_clear";
div.appendChild(clr);
alerter.appendChild(div);
$$.prototype.setDialogPosition();
if(action){
// btn.onclick = action;
$$LIB.prototype.setEvent(btn,"click",action);
$$LIB.prototype.setEvent(btn,"click",$$.prototype.delBG);
}
else{
// btn.onclick = $$.prototype.delBG;
$$LIB.prototype.setEvent(btn,"click",$$.prototype.delBG);
}
};
$$.prototype.setDialogPosition = function(){
var dialog = document.querySelector("#alerter > .alerter_dialog");
if(dialog === null){return;}
var pos = $$.prototype.getCenterPosisition(dialog);
dialog.style.setProperty("top" , pos.y +"px", "");
dialog.style.setProperty("left", pos.x +"px", "");
};
$$.prototype.getCenterPosisition = function(elm){
var eW = elm.offsetWidth,
eH = elm.offsetHeight,
sX = window.pageXOffset,
sY = window.pageYOffset,
wW = window.innerWidth,
wH = window.innerHeight;
return {
x:(wW - eW)/2 + sX,
y:(wH - eH)/2 + sY
};
};
var $$LIB = function(){};
$$LIB.prototype.setEvent = function(target, mode, func){
//other Browser
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)});
}
};
window.$$ALERTER = $$;
})();
// 実行サンプル
// $$ALERTER("hoge");
使い方
1. ソースコードを全てコピーしてブラウザコンソールに貼り付けます。
2. 実行サンプルのコマンドをブラウザコンソールで呼び出す事でalert文言が画面に表示されます。
3. 「OK」ボタンを押さないと、そのタブ内では、何もできなくなります。※ただし、スクロールしたり、キーボード操作は可能です。
もう少しデザイン変更したい場合は、JS内部にCSSを記述している箇所があるので、そちらを変更するといいでしょう。
デバッグコンソールは表示確認の為に使用しましたが、本来であれば、このJSをページ内で予め読み込んでおき、今までalert処理をしていたところで、「$$ALERTER()」関数を実行するだけというシンプルさです。
解説
表示されたエレメントは、常に画面の中央にくるように計算しています。
画面表示位置や、ダイアログデザインなどをoption値受け渡しでコントロールしたい場合は、関数定義の箇所に機能追加する必要がありますね。
要望があれば、修正したいと思います。
あと、一つ言い忘れましたが、実行関数の第2要素は、actionとしてコールバック関数を定義する事ができるようにしています。
「OK」ボタンを押した時の処理でダイアログを閉じる前に実行できる関数を記述できます。
書き方は、"function(){...}"と書くか、関数名を登録すればいいだけです。
こmの機能を使うと、confirmなどにも対応できると思いますが、confirm,promptなどは、少し拡張させて見たいとも思ってます。
懸念点
スクロールイベントを設定している箇所が、$$ALERTERを実行する時に、毎回セットされてしまっているので、この点を修正しないと、alert表示が増えてくると、無駄なscrollイベント処理が走ってしまうという事になります。
この点は現時点でissueとして考えていますので、修正版ができたら、コメントでアップします。
また、画面の要素にさわれないように、bodyタグ直下にbgエレメントを設置していますが、この要素は、CSSで画面(ページ)全体を覆い隠すようにするため、bodyタグとhtmlタグのheight style属性を変更しています。
システムページでheight値が必要なページの場合は、表示トラブルになる可能性もあるので、事前にデバッグコンソールでテストしてからお使いください。
これも、いい方法があれば、修正したいと思います。
0 件のコメント:
コメントを投稿