0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScript 第12回 ウィンドウをドラッグ&ドロップの改善・注意事項(リメイク版)

Posted at

はじめに

今回は、第11回の記事のリメイク版です。
アドバイスをいただきましたので、それをマージした記事です。
ところどころ記載を修正させていただきましたが、一番最後の「ポインターがbodyの範囲外に出た場合」が追加箇所です。

第9回、10回と作成したウィンドウをドラッグ&ドロップのソースについて、もう少し改善の余地があり、また、注意することもあるなと思いましたので、まとめます。
なお、第10回のソースをベースとしますので、第9回、第10回を見ていることを前提として記載します。

今回実施する内容

  • HTMLのbodyの範囲の確認
  • pointermoveが該当要素の外へ出た場合の対応
    drag時の課題対応.gif

ソースコード(Git Hub)

環境

OS: Windows 11 JP (64bit)
Microsoft Edge:バージョン 121.0.2277.98 (公式ビルド) (64 ビット)

参考

position

用語

課題と原因の推測

第9回、10回と作成したウィンドウをドラッグ&ドロップを速く実施したらどうなるか?特にpointermoveの部分がどうなるのか?というのが疑問でした。
pointmoveのイベントの発生とJavaScriptによる画面処理では処理が間に合うのか?ということ疑問でした。
drag時の課題.gif

第10回のソースコードで実際に試してみると、

  • 要素の中心に近いところで、速くポインターを動かしても問題なさそう
  • 要素の端で、速くポインターを動かすとウィンドウの移動が止まってしまう

という状況でした。
pointmoveのイベント発生の解像度がどのくらいなのか?と気になって調べてみましたが、具体的なものはよくわかりませんでした。

課題は2つ目の ウィンドウの移動が止まってしまう です。
この動作の原因を考えてみると、

  • pointermoveイベント発生して要素が移動するよりも早くにポインターが移動し、ポインターが要素の外に出てしまう

ためだと思いました。そのため、

  • pointermoveイベントを画面全体のbodyに付与すれば、ポインターが要素の外に出ることはなくなり、この課題は解決するのでは?

と考えました。
でも、色々試してみると、課題はもうひとつあって、

  • 画面下端で、pointerleaveイベントの発生によってpointermoveイベント発生を止めてしまう

というのもありました。
pointerleavebodyに対してEventListenerを追加していました。しかし、body=ブラウザ画面内ではなく、ブラウザ画面の下部はbodyの外の可能性があるということでした。

ということで、課題は、

  • 要素の端で、速くポインターを動かすとウィンドウの移動が止まってしまう
  • 画面下端で、pointerleaveイベントの発生によってpointermoveイベント発生を止めてしまう

です。

bodyの範囲

順番が変わってしまいますが、先にbodyの範囲を確認していきます。
結論から言えば、デフォルトでは、

  • width:ブラウザの画面幅
  • height:ソースで記載された内容の最下位の位置

のようです。
注意が必要なのは、

  • positionの設定によっては、bodyの対象かそうでないかは変わる
    • staticrelativeのような通常のフローになるものはbodyの範囲の対象ですが、absolutefixedのようなものはbodyの範囲の対象外のようで範囲だったりする

です。
positionについてはよくわからないと思いますので、これから説明していきます。

positionについては、JavaScript(でもほぼCSS) 第7回 positionの動作参照ください。

bodyの範囲調査

bodyの範囲について確認します。
第10回のソースをもとにして、

  • 「画面上部表示」を押下すると、コンソールにbodyの幅、高さを表示するようにします。bodyの幅や高さは未設定、すなわち、デフォルトの状態です。
  • また、topDivdiv要素を押下して、ポインターをbody範囲外に移動すると、その時のポインター位置をコンソールに表示します。

画面のイメージは「課題」のところのGIFイメージを参照ください。

body_range.htmlのソースコード
body_range.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>body_range</title>
    <link rel="stylesheet" href="body_range.css" type="text/css">
    <script src="body_range.js" defer></script>
</head>
<body>
    <div background-color="red">- 画面表示 -</div>
    <div id="showTop">画面上部表示</div>
    <div id="topDiv">
        <div class="closeCircle" id="closeTop">×</div>
        画面の上に表示されます。
    </div>
</body>
</html>
body_range.cssのソースコード
body_range.css
.closeCircle {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  font-size: 16px;
  line-height: 18px;
  text-align: center;
  position: absolute;
  right: 3px;
  top: 2px;
}

#topDiv .closeCircle {
  background-color: orange;
}

#topDiv {
  visibility: visible;
  background-color:yellow;
  position: absolute;
  width: 300px;
  height: 100px;
  top: 0px;
  left: 20%;
}
body_range.jsのソースコード
body_range.js
/** modeless用の設定 */
document.getElementById("showTop").addEventListener("click", () => {
    showWindow("topDiv");
});

document.getElementById("closeTop").addEventListener("click", () => {
    closeWindow("topDiv");
});

function showWindow(divId) {
    document.getElementById(divId).style.visibility = "visible";
    console.log(`body offsetwidth:${document.body.offsetWidth}`);
    console.log(`body offsetHeight:${document.body.offsetHeight}`);
}

function closeWindow(divId) {
    document.getElementById(divId).style.visibility = "hidden";
}

/** ドラッグ&ドロップで移動する設定*/
var elemId;             //ドラッグした要素のId
var elemOffsetX;        //ドラッグした要素のoffsetX
var elemOffsetY;        //ドラッグした要素のoffsetY

//"topDiv"にマウスダウン、マウスアップ時のEventLisnterを追加。
document.getElementById("topDiv").addEventListener("pointerdown", handlePointerDown);
//document.getElementById("topDiv").addEventListener("pointerup", handlePointerUp);

/**
 * マウス、もしくは、タッチで要素がアクティブになった時に、
 * - その座標の保存。
 * - ドラッグによる移動時のEventListnerを追加。×画面の上に表示されます。
 * - ドラッグしながらポインタが画面外移動時のEventListenerを追加。
 * 
 * @param {object} event コールバック関数は発生したイベントを説明するEventに基づくオブジェクト。
 */
function handlePointerDown(event) {
    getElemOffset(event);
//    document.getElementById(elemId).addEventListener("pointermove", dragWindow);
    document.body.addEventListener("pointerleave", showPointerPos);
}

/**
 * 要素のオフセット(offsetX、offsetY)、およびその要素のIdを保存。
 * 
 * @param {object} event コールバック関数は発生したイベントを説明するEventに基づくオブジェクト。
 */
function getElemOffset(event) {
    elemOffsetX = event.offsetX;
    elemOffsetY = event.offsetY;
    elemId = event.target.id;
}

/**
 * body外移動時にポインターの位置を表示。
 * 
 * @param {object} event コールバック関数は発生したイベントを説明するEventに基づくオブジェクト。
 */
function showPointerPos(event) {
    console.log(`pointer pageX:${event.pageX}`);
    console.log(`pointer pageY:${event.pageY}`);
}

画面上部表示時に実行する処理は以下の部分です。

function showWindow(divId) {
    document.getElementById(divId).style.visibility = "visible";
    console.log(`body offsetwidth:${document.body.offsetWidth}`);
    console.log(`body offsetHeight:${document.body.offsetHeight}`);
}

document.body.offsetWidthdocument.body.offsetHeightの値をコンソールに表示します。
「画面上部表示」を押下すると、今回の私の環境ではコンソールにbodyの幅と高さが表示されました。

body offsetwidth:818
body offsetHeight:48

image.png

赤枠部分がbodyの範囲で、黒枠がブラウザの画面範囲です。
これをみてわかるように、黄色のdiv要素はbodyの範囲ではないのです。この要素はCSSでposition:absoluteとしたため、要素は文書の通常のフローから除外されているのだと思います。

topDivdiv要素を押下後ポインターをbody範囲外に移動したときには、コンソールにポインター位置が表示されます(div要素はドラッグ&ドロップできるようには構成しておりませんのでご注意ください)。今回の私の環境では、以下のような感じでした。

pointer pageX:155
pointer pageY:58

ポインターを別の場所に移動すると、

pointer pageX:303
pointer pageY:100

でした。この2回分のpointerleaveイベントが発生した場所はそれぞれ違います。
image.png
赤丸で示したところが発生した場所です。
1つ目の「pageX:155、pageY:58」のところは、document.body.offsetHeight=48であることを考えると、多少値が大きいですが、まあ妥当な値です。
2つ目の「pageX:303、pageY:100」のところは、document.body.offsetHeight=48であることを考えると、???となりませんか?
調査してみると、このtopDivdiv要素も通常のフローからは除外されているものの、bodyの一部のようです。
したがって、bodyの範囲は、以下の図の赤枠のようになります。
image.png
ちなみに、topDivdiv要素をもっと下に配置して本来のbodyよりも下に配置しても、このtopDivの要素はbodyの一部で変わりません。

pointermoveによる要素の移動が追い付かず、ポインターが要素の外に出てしまう動作

「課題と原因の推測」に載せているアニGIFの動作を、「bodyの範囲調査」をふまえてみてみると、ポインターが要素の外に出てしまう動作には2つの要因がありそうだと想像つきました。
すなわち、要素の移動が追い付かない動作は、以下の時に発生しそうということです。

  • ポインターが要素の外にでた場合
  • ポインターがbodyの範囲外に出た場合

それぞれについて対策をこれからまとめます。

ポインターが要素の外に出た場合の対策

第9回で本動作を作成した時の想定動作は、

  • ドラッグでウィンドウ移動開始
  • ドラッグ中ウィンドウの移動
  • ドロップでウィンドウ移動終了

でした。
ウィンドウの移動がポインターの移動に追い付いていないようです。pointermoveを実装している要素をdiv要素に限定しており、要素の外にポインターが出てしまうと思われるため、pointermoveを実装する要素をdiv要素ではなく、bodyにすればよい と思いました。
ということで、第10回のJavaScriptのソースに以下の4点の修正を加えます。

  • pointeruptopDivdiv要素からbodyへ実装するように変更
  • handlePointerDown関数内のpointermoveelemId(要するにtopDiv)からbodyへ実装するように変更
  • handlePointerUp関数内のpointermoveelemId(要するにtopDiv)からbodyへ実装削除するように変更
  • cancelDragWindow関数内のpointermoveelemId(要するにtopDiv)からbodyへ実装削除するように変更

また、htmlのソースもbodyの範囲を拡張するため、<br>で改行を追加します。

modeless_drag_pointer2.htmlのソース(第10回から変更あり)

主な変更は、bodyの範囲を拡張するため、<br>で改行を追加であり、改行の追加自体は課題解決の本質ではなく、ポインターの移動が要素の外でbodyの範囲内になるようにするためです。
また、bodyの範囲外になるような場合は引き続き課題が起こることを確認できるような程度の改行追加としています。

modeless_drag_pointer2.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>dragpointer2</title>
    <link rel="stylesheet" href="modeless_drag_pointer2.css" type="text/css">
    <script src="modeless_drag_pointer2.js" defer></script>
</head>
<body>
    <div>- 画面表示 -</div>
    <div id="showTop">画面上部表示</div>
    <div id="topDiv">
        <div class="closeCircle" id="closeTop">×</div>
        画面の上に表示されます。
    </div>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
</body>
</html>
modeless_drag_pointer2.cssのソース(第10回から変更なし)
modeless_drag_pointer2.css
.closeCircle {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  font-size: 16px;
  line-height: 18px;
  text-align: center;
  position: absolute;
  right: 3px;
  top: 2px;
}

#topDiv .closeCircle {
  background-color: orange;
}

#topDiv {
  visibility: hidden;
  background-color:yellow;
  position: absolute;
  width: 300px;
  height: 100px;
  top: 0px;
  left: 20%;
}
modeless_drag_pointer2.js(第10回から変更あり)
modeless_drag_pointer2.js
/** modeless用の設定 */
document.getElementById("showTop").addEventListener("click", () => {
    showWindow("topDiv");
});

document.getElementById("closeTop").addEventListener("click", () => {
    closeWindow("topDiv");
});

function showWindow(divId) {
    document.getElementById(divId).style.visibility = "visible";
}

function closeWindow(divId) {
    document.getElementById(divId).style.visibility = "hidden";
}

/** ドラッグ&ドロップで移動する設定*/
var elemId;             //ドラッグした要素のId
var elemOffsetX;        //ドラッグした要素のoffsetX
var elemOffsetY;        //ドラッグした要素のoffsetY

//"topDiv"にマウスダウン、マウスアップ時のEventLisnterを追加。
document.getElementById("topDiv").addEventListener("pointerdown", handlePointerDown);
document.body.addEventListener("pointerup", handlePointerUp);

/**
 * マウス、もしくは、タッチで要素がアクティブになった時に、
 * - その座標の保存。
 * - ドラッグによる移動時のEventListnerを追加。×画面の上に表示されます。
 * - ドラッグしながらポインタが画面外移動時のEventListenerを追加。
 * 
 * @param {object} event コールバック関数は発生したイベントを説明するEventに基づくオブジェクト。
 */
function handlePointerDown(event) {
    getElemOffset(event);
    document.body.addEventListener("pointermove", dragWindow);
    document.body.addEventListener("pointerleave", cancelDragWindow);
}

/**
 * マウス、もしくは、タッチで要素が非アクティブになった時に、
 * - ドラッグによる移動時のEventListenerを削除。
 * 
 * @param {object} event コールバック関数は発生したイベントを説明するEventに基づくオブジェクト。
 */
function handlePointerUp(event) {
    document.body.removeEventListener("pointermove", dragWindow);
    document.body.removeEventListener("pointerleave", cancelDragWindow);
}

/**
 * 要素のオフセット(offsetX、offsetY)、およびその要素のIdを保存。
 * 
 * @param {object} event コールバック関数は発生したイベントを説明するEventに基づくオブジェクト。
 */
function getElemOffset(event) {
    elemOffsetX = event.offsetX;
    elemOffsetY = event.offsetY;
    elemId = event.target.id;
}

/**
 * マウス、もしくは、タッチで要素をドラッグ時に、ドラッグした要素を移動。
 * 
 * @param {object} event コールバック関数は発生したイベントを説明するEventに基づくオブジェクト。
 */
function dragWindow(event) {
    document.getElementById(elemId).style.left = event.pageX - elemOffsetX + "px";
    document.getElementById(elemId).style.top = event.pageY - elemOffsetY + "px";
}

/**
 * body外移動時のdragWindowのEventListenerを削除。
 * 
 * @param {object} event コールバック関数は発生したイベントを説明するEventに基づくオブジェクト。
 */
function cancelDragWindow(event) {
    document.body.removeEventListener("pointermove", dragWindow);
    document.body.removeEventListener("pointerleave", cancelDragWindow);
}

上記を動作させると以下の通りになります。
drag時の課題対応.gif

bodyの範囲内であれば、ポインターを速く動作させてもウィンドウは追従して動作します。
しかし、bodyの範囲外の場合、ポインターを速く動作させるとウィンドウは追従せず止まります(<br>bodyの範囲を広げましたが、それより下にポインターが移動すると対応できないということです)。
この課題については、これから説明します。

ポインターがbodyの範囲外に出た場合

ポインターがbodyの範囲外に出た場合の対処というよりは、ポインターがbodyの範囲外に出ないようにする対処について、アドバイスをいただきましたので、それを試していきたいと思います。

  • bodyの範囲を100%にする対処
  • bodyではなくwindowaddEventHandlerを付与する対処

bodyの範囲を100%にする対処

「ポインターが要素の外に出た場合の対策」のソースをもとに、bodyの範囲を100%にします。
height100vhに設定します。
widthは現状のソースだとデフォルトでは画面の幅になるため、対応なしとします。

modeless_drag_pointer3.htmlのソース(modeless_drag_pointer2.htmlからほぼ変更なし)
  • CSSとJavaScriptファイルのファイル名だけ変更しています。
modeless_drag_pointer3.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>dragpointer2</title>
    <link rel="stylesheet" href="modeless_drag_pointer3.css" type="text/css">
    <script src="modeless_drag_pointer3.js" defer></script>
</head>
<body>
    <div>- 画面表示 -</div>
    <div id="showTop">画面上部表示</div>
    <div id="topDiv">
        <div class="closeCircle" id="closeTop">×</div>
        画面の上に表示されます。
    </div>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
</body>
</html>
modeless_drag_pointer3.cssのソース(modeless_drag_pointer2.cssから変更あり)
  • bodyheight:100vhを追加しています。
modeless_drag_pointer3.css
body {
  height: 100vh;
}

.closeCircle {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  font-size: 16px;
  line-height: 18px;
  text-align: center;
  position: absolute;
  right: 3px;
  top: 2px;
}

#topDiv .closeCircle {
  background-color: orange;
}

#topDiv {
  visibility: hidden;
  background-color:yellow;
  position: absolute;
  width: 300px;
  height: 100px;
  top: 0px;
  left: 20%;
}
modeless_drag_pointer3.js(modeless_drag_pointer2.jsから変更なし)
modeless_drag_pointer3.js
/** modeless用の設定 */
document.getElementById("showTop").addEventListener("click", () => {
    console.log(`body offsetwidth:${document.body.offsetWidth}`);
    console.log(`body offsetHeight:${document.body.offsetHeight}`);

    showWindow("topDiv");
});

document.getElementById("closeTop").addEventListener("click", () => {
    closeWindow("topDiv");
});

function showWindow(divId) {
    document.getElementById(divId).style.visibility = "visible";
}

function closeWindow(divId) {
    document.getElementById(divId).style.visibility = "hidden";
}

/** ドラッグ&ドロップで移動する設定*/
var elemId;             //ドラッグした要素のId
var elemOffsetX;        //ドラッグした要素のoffsetX
var elemOffsetY;        //ドラッグした要素のoffsetY

//"topDiv"にマウスダウン、マウスアップ時のEventLisnterを追加。
document.getElementById("topDiv").addEventListener("pointerdown", handlePointerDown);
document.body.addEventListener("pointerup", handlePointerUp);

/**
 * マウス、もしくは、タッチで要素がアクティブになった時に、
 * - その座標の保存。
 * - ドラッグによる移動時のEventListnerを追加。×画面の上に表示されます。
 * - ドラッグしながらポインタが画面外移動時のEventListenerを追加。
 * 
 * @param {object} event コールバック関数は発生したイベントを説明するEventに基づくオブジェクト。
 */
function handlePointerDown(event) {
    getElemOffset(event);
    document.body.addEventListener("pointermove", dragWindow);
    document.body.addEventListener("pointerleave", cancelDragWindow);
}

/**
 * マウス、もしくは、タッチで要素が非アクティブになった時に、
 * - ドラッグによる移動時のEventListenerを削除。
 * 
 * @param {object} event コールバック関数は発生したイベントを説明するEventに基づくオブジェクト。
 */
function handlePointerUp(event) {
    document.body.removeEventListener("pointermove", dragWindow);
    document.body.removeEventListener("pointerleave", cancelDragWindow);
}

/**
 * 要素のオフセット(offsetX、offsetY)、およびその要素のIdを保存。
 * 
 * @param {object} event コールバック関数は発生したイベントを説明するEventに基づくオブジェクト。
 */
function getElemOffset(event) {
    elemOffsetX = event.offsetX;
    elemOffsetY = event.offsetY;
    elemId = event.target.id;
}

/**
 * マウス、もしくは、タッチで要素をドラッグ時に、ドラッグした要素を移動。
 * 
 * @param {object} event コールバック関数は発生したイベントを説明するEventに基づくオブジェクト。
 */
function dragWindow(event) {
    document.getElementById(elemId).style.left = event.pageX - elemOffsetX + "px";
    document.getElementById(elemId).style.top = event.pageY - elemOffsetY + "px";
}

/**
 * body外移動時のdragWindowのEventListenerを削除。
 * 
 * @param {object} event コールバック関数は発生したイベントを説明するEventに基づくオブジェクト。
 */
function cancelDragWindow(event) {
    document.body.removeEventListener("pointermove", dragWindow);
    document.body.removeEventListener("pointerleave", cancelDragWindow);
}

上記を動作させると、ブラウザの右にスクロールバーは表示されますが、ブラウザ画面内の下のほうもbodyとなり、ポインターがbodyの範囲外に出ないようにすることができました。
合格.gif
100vhにしたときにスクロールバーが表示される理由は、アドレスバー分を含んで100vhになるとういことで、アドレスバー分を減らした98vhくらいにするとスクロールバー自体は消すことができました。
もともとのhtmlが長いようならスクロールバーを消すことはできませんので、あまり気にすることではないかもしれません。

bodyではなくwindowaddEventHandlerを付与する対処

「ポインターが要素の外に出た場合の対策」のソースをもとに、bodyに付与していたpointermovepointerup、および、pointerleaveaddEventHandlerwindowに付与します。

modeless_drag_pointer4.htmlのソース(modeless_drag_pointer2.htmlからほぼ変更なし)
  • CSSとJavaScriptファイルのファイル名だけ変更しています。
modeless_drag_pointer4.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>dragpointer2</title>
    <link rel="stylesheet" href="modeless_drag_pointer4.css" type="text/css">
    <script src="modeless_drag_pointer4.js" defer></script>
</head>
<body>
    <div>- 画面表示 -</div>
    <div id="showTop">画面上部表示</div>
    <div id="topDiv">
        <div class="closeCircle" id="closeTop">×</div>
        画面の上に表示されます。
    </div>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
</body>
</html>
modeless_drag_pointer4.cssのソース(modeless_drag_pointer2.cssから変更なし)
modeless_drag_pointer4.css
.closeCircle {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  font-size: 16px;
  line-height: 18px;
  text-align: center;
  position: absolute;
  right: 3px;
  top: 2px;
}

#topDiv .closeCircle {
  background-color: orange;
}

#topDiv {
  visibility: hidden;
  background-color:yellow;
  position: absolute;
  width: 300px;
  height: 100px;
  top: 0px;
  left: 20%;
}
modeless_drag_pointer4.js(modeless_drag_pointer2.jsから変更あり)
  • bodyに付与していたpointermovepointerup、および、pointerleaveaddEventHandlerwindowに付与します。
modeless_drag_pointer4.js
/** modeless用の設定 */
document.getElementById("showTop").addEventListener("click", () => {
    console.log(`body offsetwidth:${document.body.offsetWidth}`);
    console.log(`body offsetHeight:${document.body.offsetHeight}`);

    showWindow("topDiv");
});

document.getElementById("closeTop").addEventListener("click", () => {
    closeWindow("topDiv");
});

function showWindow(divId) {
    document.getElementById(divId).style.visibility = "visible";
}

function closeWindow(divId) {
    document.getElementById(divId).style.visibility = "hidden";
}

/** ドラッグ&ドロップで移動する設定*/
var elemId;             //ドラッグした要素のId
var elemOffsetX;        //ドラッグした要素のoffsetX
var elemOffsetY;        //ドラッグした要素のoffsetY

//"topDiv"にマウスダウン、マウスアップ時のEventLisnterを追加。
document.getElementById("topDiv").addEventListener("pointerdown", handlePointerDown);
window.addEventListener("pointerup", handlePointerUp);

/**
 * マウス、もしくは、タッチで要素がアクティブになった時に、
 * - その座標の保存。
 * - ドラッグによる移動時のEventListnerを追加。×画面の上に表示されます。
 * - ドラッグしながらポインタが画面外移動時のEventListenerを追加。
 * 
 * @param {object} event コールバック関数は発生したイベントを説明するEventに基づくオブジェクト。
 */
function handlePointerDown(event) {
    getElemOffset(event);
    window.addEventListener("pointermove", dragWindow);
    window.addEventListener("pointerleave", cancelDragWindow);
}

/**
 * マウス、もしくは、タッチで要素が非アクティブになった時に、
 * - ドラッグによる移動時のEventListenerを削除。
 * 
 * @param {object} event コールバック関数は発生したイベントを説明するEventに基づくオブジェクト。
 */
function handlePointerUp(event) {
    window.removeEventListener("pointermove", dragWindow);
    window.removeEventListener("pointerleave", cancelDragWindow);
}

/**
 * 要素のオフセット(offsetX、offsetY)、およびその要素のIdを保存。
 * 
 * @param {object} event コールバック関数は発生したイベントを説明するEventに基づくオブジェクト。
 */
function getElemOffset(event) {
    elemOffsetX = event.offsetX;
    elemOffsetY = event.offsetY;
    elemId = event.target.id;
}

/**
 * マウス、もしくは、タッチで要素をドラッグ時に、ドラッグした要素を移動。
 * 
 * @param {object} event コールバック関数は発生したイベントを説明するEventに基づくオブジェクト。
 */
function dragWindow(event) {
    document.getElementById(elemId).style.left = event.pageX - elemOffsetX + "px";
    document.getElementById(elemId).style.top = event.pageY - elemOffsetY + "px";
}

/**
 * body外移動時のdragWindowのEventListenerを削除。
 * 
 * @param {object} event コールバック関数は発生したイベントを説明するEventに基づくオブジェクト。
 */
function cancelDragWindow(event) {
    window.removeEventListener("pointermove", dragWindow);
    window.removeEventListener("pointerleave", cancelDragWindow);
}

上記を動作させると、bodyの範囲を100%にする対処と同様にブラウザ画面内の下のほうでも問題なく動作しました。そのうえ、スクロールバーも表示されませんでした。
この実装のほうが期待にそう結果になったと思いました。

おわりに

今回はウィンドウのドラッグ&ドロップの動作に高速ポインター移動にある程度対応できるようにソースの改善を行いました。
第11回で解決させなかったbodyの範囲外に移動した場合の動作についてもアドバイスをいただき解消できました。

0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?