Created
September 30, 2013 07:02
-
-
Save edom18/6760216 to your computer and use it in GitHub Desktop.
移動可能なポップアップを実装してみる
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#簡易なポップアップウィンドウを実装する | |
比較的シンプルなポップアップウィンドウを実装します。 | |
できることはYES/NOボタンの配置、コンテンツとタイトルの変更、タイトル部分ドラッグでの移動のみです。 | |
こちらは記事用サンプルです。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@import "compass/reset"; | |
body { | |
font-size: 13px; | |
line-height: 1.5; | |
} | |
strong { | |
font-weight: bold; | |
} | |
#container { | |
padding: 10px; | |
textarea, input[type=text] { | |
padding: 8px 10px; | |
width: 250px; | |
border: solid 1px #999; | |
border-radius: 4px; | |
} | |
input[type="button"] { | |
cursor: pointer; | |
padding: 5px 10px; | |
border: solid 1px #666; | |
background-color: #ccc; | |
box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.55); | |
font-weight: bold; | |
&:hover { | |
background-color: #eee; | |
} | |
} | |
} | |
#log { | |
position: absolute; | |
left: 0; | |
bottom: 0; | |
width: 100%; | |
height: 150px; | |
overflow: auto; | |
border: solid 1px #ccc; | |
background: #111; | |
padding: 5px; | |
box-sizing: border-box; | |
color: white; | |
.weak { | |
color: #777; | |
} | |
#console { | |
padding: 2px 10px; | |
background: url(http://jsrun.it/assets/3/v/q/e/3vqet.png) left top repeat-y; | |
} | |
} | |
//----------------------------------------------- | |
// 以下からポップアップウィンドウ自体のスタイル設定 | |
//----------------------------------------------- | |
.popwindow { | |
position: absolute; | |
left: 0; | |
top: 0; | |
z-index: 1000; | |
width: 300px; | |
padding: 15px; | |
background-color: white; | |
border: solid 1px #999; | |
box-shadow: 0 0 8px 2px rgba(0, 0, 0, 0.3); | |
.popwindow-title { | |
cursor: move; | |
margin: -15px -15px 10px; | |
padding: 10px; | |
color: #fff; | |
background-color: #333; | |
border-bottom: solid 1px #000; | |
-webkit-user-select: none; | |
user-select: none; | |
} | |
.popwindow-content { | |
margin-bottom: 10px; | |
padding: 5px; | |
} | |
.popwindow-buttons { | |
text-align: center; | |
p { | |
display: inline-block; | |
} | |
input { | |
cursor: pointer; | |
width: 80px; | |
padding: 5px 0; | |
border: solid 1px #034472; | |
background-color: #1885d1; | |
box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.55); | |
font-weight: bold; | |
&:hover { | |
border-color: #0a7792; | |
background-color: #17adde; | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- ポップアップウィンドウ設定項目 --> | |
<div id="container"> | |
<p><input type="text" id="title" value="タイトル部分をドラッグすると移動できます" /></p> | |
<p><textarea name="content" id="content" cols="30" rows="10">テキストエリアに文字を入力し、「<strong>ポップアップウィンドウを表示</strong>」ボタンを押すとそのテキストの内容がポップアップウィンドウに表示されます。</textarea></p> | |
<p> | |
<input type="checkbox" id="isModal" checked="true" />モーダルで表示 | |
<input type="checkbox" id="isOnlyYes" checked="true" />YESボタンのみ | |
</p> | |
<p> | |
<input type="button" id="createWindow" value="ポップアップウィンドウを表示" /> | |
</p> | |
</div> | |
<!-- ログ出力用エリア --> | |
<div id="log"> | |
<div id="console"> | |
<p class="weak">ここにログが出力されます。</p> | |
</div> | |
</div> | |
<!-- ポップアップウィンドウ用ベースHTML(テンプレート) --> | |
<script id="template-window" type="text/x-template"> | |
<div class="popwindow-title"></div> | |
<div class="popwindow-content"></div> | |
<div class="popwindow-buttons"> | |
<p class="popwindow-button-yes"><input type="button" value="Yes" /></p> | |
<p class="popwindow-button-no"><input type="button" value="No" /></p> | |
</div> | |
</script> | |
<!-- ポップアップウィンドウの起動処理 --> | |
<script> | |
//ログ出力機能の実装 | |
var logEl = document.getElementById('console'); | |
var cnt = 0; | |
function log(mes) { | |
var p = document.createElement('p'); | |
var before = logEl.querySelector('p:first-child'); | |
p.appendChild(document.createTextNode(++cnt + ': ' + mes)); | |
logEl.insertBefore(p, before); | |
} | |
//ブラウザのload完了時にポップアップウィンドウを表示する | |
window.onload = function () { | |
function createWindow() { | |
var title = document.getElementById('title').value; | |
var text = document.getElementById('content').value; | |
var isModal = document.getElementById('isModal').checked; | |
var isOnlyYes = document.getElementById('isOnlyYes').checked; | |
var conf = { | |
title : title, | |
content: text, | |
modal : isModal, | |
buttonType: isOnlyYes ? Popwindow.buttonType.YES : Popwindow.buttonType.YESNO | |
}; | |
var pop = new Popwindow(conf); | |
pop.addListener('close', function () { | |
log('ポップアップウィンドウが閉じられました。'); | |
}); | |
pop.addListener('yes', function () { | |
log('Yesが押されました!'); | |
}); | |
pop.addListener('no', function () { | |
log('Noが押されました!'); | |
}); | |
return pop; | |
} | |
var btn = document.getElementById('createWindow'); | |
//クリックすると新しいポップアップウィンドウを生成 | |
btn.addEventListener('click', function () { | |
createWindow(); | |
}, false); | |
var popTutorial = createWindow(); | |
//最初のチュートリアルが閉じたタイミングでYESボタンのみフラグをオフに。 | |
popTutorial.addListener('close', function () { | |
var isOnlyYes = document.getElementById('isOnlyYes'); | |
isOnlyYes.checked = false; | |
}); | |
}; | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Mouse capture(マウス位置のキャプチャ用オブジェクト) | |
* @param {Element} target ドラッグ位置を検知したい要素 | |
* @param {Function} callback 位置を計算したあとに呼び出されるコールバック関数 | |
*/ | |
function MouseCapture(target, callback) { | |
this._target = target; | |
//callback関数が指定されていない場合は空の関数を保存 | |
this._callback = callback || function (x, y) {}; | |
this._dragging = false; //ドラッグ状態。最初はオフ。 | |
//ドラッグ位置の前回の位置を保存。初期値はx, yともに0。 | |
this._previous = { x: 0, y: 0 }; | |
//各種イベントにイベントハンドラを登録 | |
var that = this; | |
target.addEventListener('mousedown', function (e) { | |
that._mousedownHandler(e); | |
}, false); | |
document.addEventListener('mousemove', function (e) { | |
that._mousemoveHandler(e); | |
}, false); | |
document.addEventListener('mouseup', function (e) { | |
that._mouseupHandler(e); | |
}, false); | |
} | |
//MouseCaptureオブジェクトのプロトタイプを設定 | |
MouseCapture.prototype = { | |
constructor: MouseCapture, | |
//mouse down時の挙動 | |
_mousedownHandler: function (e) { | |
this._dragging = true; | |
this._previous.x = e.pageX; | |
this._previous.y = e.pageY; | |
}, | |
//mouse move時の挙動 | |
_mousemoveHandler: function (e) { | |
if (!this._dragging) { | |
return; | |
} | |
var x = e.pageX - this._previous.x; | |
var y = e.pageY - this._previous.y; | |
this._previous = { | |
x: e.pageX, | |
y: e.pageY | |
}; | |
this._callback(x, y); | |
}, | |
//mouse up時の挙動 | |
_mouseupHandler: function (e) { | |
this._dragging = false; | |
} | |
}; | |
/** | |
* モーダルレイヤーを表示する | |
*/ | |
function ModalLayer() { | |
this._init(); | |
} | |
//ModalLayerオブジェクトのプロトタイプを設定 | |
ModalLayer.prototype = { | |
constructor: ModalLayer, | |
//モーダルレイヤーの初期化処理 | |
_init: function () { | |
this.el = document.createElement('div'); | |
this.el.style.cssText = 'position: fixed;' + | |
'left: 0;' + | |
'top: 0;' + | |
'z-index: 900;' + | |
'width: 100%;' + | |
'height: 100%;' + | |
'background-color: rgba(0, 0, 0, 0.7);'; | |
document.body.appendChild(this.el); | |
}, | |
//モーダルレイヤーを閉じる | |
close: function () { | |
this.el.parentNode.removeChild(this.el); | |
this.el = null; | |
} | |
}; | |
/** | |
* ポップアップウィンドウを生成する | |
* @param {Object} conf ポップアップウィンドウの設定項目 | |
* {Object.<string>} content 表示内容 | |
* {Object.<string>} title タイトル | |
* {Object.<boolean>} modal モーダルのON/OFF | |
* {Object.<enum.<number>>} buttonType ボタンタイプ。YES, YES/NOの2タイプ | |
*/ | |
function Popwindow(conf) { | |
conf || (conf = { | |
title: 'No title', | |
content: '', | |
modal: false, | |
buttonType: Popwindow.buttonType.YESNO | |
}); | |
this._init(conf); | |
this._show(); | |
} | |
//ボタンタイプの種類を定義 | |
Popwindow.buttonType = { YES: 1, YESNO: 2 }; | |
//Popwindowオブジェクトのプロトタイプを設定 | |
Popwindow.prototype = { | |
constructor: Popwindow, | |
/** | |
* ポップアップウィンドウの初期化 | |
* @param {Object} conf ポップアップウィンドウの設定情報 | |
*/ | |
_init: function (conf) { | |
this.el = document.createElement('div'); | |
this.el.appendChild(this._buildWindow(conf.title, conf.content)); | |
this.el.className = 'popwindow'; | |
this.left = 0; | |
this.top = 0; | |
//thisを変数に保存 | |
var that = this; | |
//Yesボタン押下時の処理 | |
var yesBtn = this.el.querySelector('.popwindow-button-yes'); | |
yesBtn.addEventListener('click', function (e) { | |
that._yesHandler(e); | |
}, false); | |
//NOボタンがあるときだけイベントを付与 | |
var noBtn = this.el.querySelector('.popwindow-button-no'); | |
if (conf.buttonType === Popwindow.buttonType.YESNO) { | |
//Noボタン押下時の処理 | |
noBtn.addEventListener('click', function (e) { | |
that._noHandler(e); | |
}, false); | |
} | |
else { | |
noBtn.style.display = 'none'; | |
} | |
//ウィンドウの移動処理を「MouseCapture」オブジェクトに委譲する | |
var titleElement = this.el.querySelector('.popwindow-title'); | |
this._mouseCapture = new MouseCapture(titleElement, function (x, y) { | |
that.moveTo(that.left + x, that.top + y); | |
}); | |
//モーダルのチェック | |
if (conf.modal === true) { | |
this.modal = new ModalLayer(); | |
} | |
}, | |
_buildWindow: function (titleText, contentText) { | |
//コンテナとなるdiv要素を生成する | |
var container = document.createElement('div'); | |
//テンプレート情報を格納している要素(<script>要素)からHTMLテキストを取得 | |
var templateElement = document.getElementById('template-window'); | |
container.innerHTML = templateElement.innerHTML; | |
//テンプレートからタイトル、コンテンツ用要素を取得 | |
var title = container.querySelector('.popwindow-title'); | |
var content = container.querySelector('.popwindow-content'); | |
//セキュリティを意識する場合はcreateTextNodeを使ったほうが安全 | |
//var titleTextNode = document.createTextNode(titleText); | |
//title.appendChild(titleTextNode); | |
//var contentTextNode = document.createTextNode(contentText); | |
//content.appendChild(contentTextNode); | |
title.innerHTML = titleText; | |
content.innerHTML = contentText; | |
//生成したコンテナ要素を返す | |
return container; | |
}, | |
//ポップアップウィンドウを表示する | |
_show: function () { | |
document.body.appendChild(this.el); | |
//追加された要素の幅と高さを取得 | |
var width = this.el.clientWidth; | |
var height = this.el.clientHeight; | |
//画面中央に来るように位置を調整 | |
var x = window.innerWidth / 2 - width / 2; | |
var y = window.innerHeight / 2 - height / 2; | |
this.moveTo(x, y); | |
}, | |
//YESが選択されたときの処理 | |
_yes: function () { | |
this.trigger('yes'); | |
this.close(); | |
}, | |
//NOが選択されたときの処理 | |
_no: function () { | |
this.trigger('no'); | |
this.close(); | |
}, | |
//YESボタンが押された時の処理 | |
_yesHandler: function () { | |
this._yes(); | |
}, | |
//NOボタンが押された時の処理 | |
_noHandler: function () { | |
this._no(); | |
}, | |
/** | |
* ポップアップウィンドウを指定されたx, yに移動する | |
* @param {number} x xの位置。(style.leftの値) | |
* @param {number} y yの位置。(style.topの値) | |
*/ | |
moveTo: function (x, y) { | |
this.left = x; | |
this.top = y; | |
this.el.style.left = x + 'px'; | |
this.el.style.top = y + 'px'; | |
}, | |
//ポップアップウィンドウを閉じる | |
close: function () { | |
if (this.modal) { | |
this.modal.close(); | |
} | |
this.el.parentNode.removeChild(this.el); | |
this.trigger('close'); | |
}, | |
/** | |
* イベントトリガー | |
* @param {string} type イベントタイプ | |
* @param {Object} data イベントリスナーに渡すデータ | |
*/ | |
trigger: function (type, data) { | |
var handlers = this._handlers || (this._handlers = {}); | |
var handler = handlers[type] || (handlers[type] = []); | |
for (var i = 0, l = handler.length; i < l; i++) { | |
handler[i](data); | |
} | |
}, | |
/** | |
* イベントリスナーの登録 | |
* @param {string} type 登録したいイベントタイプ | |
* @param {Function} callback イベント発火時のコールバック関数 | |
*/ | |
addListener: function (type, callback) { | |
var handlers = this._handlers || (this._handlers = {}); | |
var handler = handlers[type] || (handlers[type] = []); | |
handler.push(callback); | |
}, | |
/** | |
* イベントリスナーの解除 | |
* @param {string} type 解除したいイベントタイプ | |
* @param {Function} callback 解除したいコールバック関数 | |
*/ | |
removeListener: function (type, callback) { | |
var handlers = this._handlers || (this._handlers = {}); | |
var handler = handlers[type] || (handlers[type] = []); | |
var len = handler.length; | |
while (len--) { | |
if (handler[len] === callback) { | |
handler.splice(len, 1); | |
break; | |
} | |
} | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment