こんにちは。前回に引き続き、
要素のドラッグ
ウェブアプリでよく使われるインタフェースのひとつ、
投稿フォームで入力する際、
<div class="js-drag" id="js-drag-1">
<form class="js-drag-form" onsubmit="return false;">
<textarea></textarea>
<input class="submit" type="submit" value="送信">
</form>
</div>
そして、
.js-drag{
position:absolute;
background:#555;
display:block;
margin:0;
padding:0;
border:0;
}
.js-drag form{
margin:15px;
}
.js-drag textarea{
width:400px;
height:240px;
display:block;
}
.js-drag input.submit{
width:400px;
height:30px;
}
さて、
var element = document.getElementById('js-drag-1');
// 要素の現在位置を取得
var rect = element.getBoundingClientRect();
// 要素をbody直下に移動
document.body.appendChild(element);
// この時、要素の位置がページ最下部に移動してしまう
var root = document.documentElement;
// 現在のスクロール量を取得
var left = window.pageXOffset || root.scrollLeft;
var top = window.pageYOffset || root.scrollTop;
// 要素の位置を戻す
element.style.left = (rect.left + left) + 'px';
element.style.top = (rect.top + top) + 'px';
var dragging = false;
element.onmousedown = function(evt){
dragging = true;
};
document.onmouseup = function(evt){
dragging = false;
};
document.onmousemove = function(evt){
if(dragging){
if(!evt){
evt = window.event;
}
var left = window.pageXOffset || root.scrollLeft;
var top = window.pageYOffset || root.scrollTop;
element.style.left = left + evt.clientX + 'px';
element.style.top = top + evt.clientY + 'px';
}
};
ドラッグの下準備として、
肝心のドラッグ処理のコードは至ってシンプルですね。マウスダウンでドラッグを開始し、
しかし、
- ドラッグを開始すると要素の位置がずれる
(要素の左上を掴んだ状態になる) - ドラッグ中にテキストを選択してしまう
- テキストエリア内の文字を選択できない
- スクロールに追従しない
(必ずしも追従したほうがよいというわけではありませんが、 今回は追従させます)
といった問題があげられます。
まず、
ドラッグ中にテキストを選択してしまう問題については、
テキストエリア内の文字を選択できない件については、
スクロールの追従については第20回で扱ったposition:fixedで実現します。
var IE6 = !window.XMLHttpRequest && window.ActiveXObject;
var element = document.getElementById('js-drag-2');
var rect = element.getBoundingClientRect();
var root = document.documentElement;
document.body.appendChild(element);
var left = window.pageXOffset || root.scrollLeft;
var top = window.pageYOffset || root.scrollTop;
element.style.left = (rect.left + left) + 'px';
element.style.top = (rect.top + top ) + 'px';
var dragging = false;
var x, y; // 相対位置を保存しておく変数
element.onmousedown = function(evt){
if(!evt){
evt = window.event;
}
var target = evt.target || evt.srcElement;
if (target === element) {
x = evt.offsetX || evt.layerX;//相対位置
y = evt.offsetY || evt.layerY;
dragging = true;
// ドラッグ中はabsoluteに
var rect = element.getBoundingClientRect();
var left = window.pageXOffset || root.scrollLeft;
var top = window.pageYOffset || root.scrollTop;
element.style.left = left + rect.left + 'px';
element.style.top = top + rect.top + 'px';
element.style.position = 'absolute';
if (IE6){
element.style.removeExpression('behavior');
}
return false;
}
};
document.onmouseup = function(evt){
dragging = false;
// ドラッグが終わったらfixedに
var rect = element.getBoundingClientRect();
element.style.left = rect.left + 'px';
element.style.top = rect.top + 'px';
element.style.position = 'fixed';
if (IE6) {
element.style.position = 'absolute';
var L = 'document.documentElement.scrollLeft';
var T = 'document.documentElement.scrollTop';
element.style.setExpression('behavior',
'this.style.left=('+rect.left+'+'+L+')+"px",'+
'this.style.top =('+rect.top +'+'+T+')+"px"');
document.body.style.backgroundImage='url(about:blank)';
document.body.style.backgroundAttachment = 'fixed';
}
};
document.onmousemove = function(evt){
if(!evt){
evt = window.event;
}
if(dragging){
var left = window.pageXOffset || root.scrollLeft;
var top = window.pageYOffset || root.scrollTop;
element.style.left = left + evt.clientX - x + 'px';
element.style.top = top + evt.clientY - y + 'px';
return false;
}
};
コードはやや複雑になりましたが、
さて、
まず、
また、
イベントを解除するためにはイベントリスナーとして登録する関数を参照可能な状態にする必要があります。具体的には、
var addEvent = document.addEventListener ?
function(node,type,listener){
node.addEventListener(type,listener,false);
} :
function(node,type,listener){
node.attachEvent('on'+type, listener);
}
var removeEvent = document.removeEventListener ?
function(node,type,listener){
node.removeEventListener(type,listener,false);
} :
function(node,type,listener){
node.detachEvent('on'+type, listener);
}
addEvent(document,'mousedown', function(evt){
addEvent(document, 'mousemove', mousemove);
});
addEvent(document, 'mouseup', function(evt){
removeEvent(document, 'mousemove', mousemove);
});
function mousemove(evt){
}
イベントを登録する際にはmousemoveという関数がそのまま登録され、
さてもう一つ、
例えば、
最後に、
function getStyle(element){
return window.getComputedStyle ?
getComputedStyle(element, '') :
element.currentStyle;
}
var IE6 = !window.XMLHttpRequest && window.ActiveXObject;
var root = document.documentElement;
var dragging = false;
var fixed = false;
var x, y, dx, dy;
var element;
addEvent(document,'mousedown', function(evt){
if(!evt){
evt = window.event;
}
var target = evt.target || evt.srcElement;
if (target.className === 'js-drag') {
x = evt.offsetX || evt.layerX;
y = evt.offsetY || evt.layerY;
dragging = true;
element = target;
var style = getStyle(element);
var rect = element.getBoundingClientRect();
var left = window.pageXOffset || root.scrollLeft;
var top = window.pageYOffset || root.scrollTop;
if (style.position === 'fixed'){
fixed = true;
dx = 0;
dy = 0;
} else {
fixed = false;
dx = (rect.left + left) - element.offsetLeft;
dy = (rect.top + top ) - element.offsetTop;
}
if (IE6){
element.style.removeExpression('behavior');
}
if (evt.preventDefault){
evt.preventDefault();
} else {
evt.returnValue = false;
}
addEvent(document, 'mousemove', mousemove);
}
});
addEvent(document, 'mouseup', function(evt){
if(dragging){
dragging = false;
removeEvent(document, 'mousemove', mousemove);
var rect = element.getBoundingClientRect();
element.style.left = rect.left + 'px';
element.style.top = rect.top + 'px';
element.style.position = 'fixed';
if (IE6) {
element.style.position = 'absolute';
var L = 'document.documentElement.scrollLeft';
var T = 'document.documentElement.scrollTop';
element.style.setExpression('behavior',
'this.style.left=(' +(rect.left-dx)+ '+' + L + ' )+"px",'+
'this.style.top =(' +(rect.top -dy)+ '+' + T + ' )+"px"');
document.body.style.backgroundImage='url(about:blank)'
document.body.style.backgroundAttachment = 'fixed';
}
}
});
function mousemove(evt){
if(!evt){
evt = window.event;
}
if(dragging){
var left= fixed? 0: window.pageXOffset||root.scrollLeft
var top = fixed? 0: window.pageYOffset||root.scrollTop
element.style.left= left+ evt.clientX - x - dx + 'px';
element.style.top = top + evt.clientY - y - dy + 'px';
if (evt.preventDefault){
evt.preventDefault();
} else {
evt.returnValue = false;
}
}
}
まとめ
今回はJavaScriptを使ったUIとしてドラッグの実装方法を紹介しました。固定配置
次回も引き続きJavaScriptを使ったクロスブラウザなUIの実装を見ていきたいと思います。