Div要素の序盤の定石探す①


Drag可能なdiv要素

己自身をDragするもの

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>DOM</title>
</head>
<body>
    <script type="module" src="main.js"></script>
</body>
</html>

main.js

const div = document.createElement('div');
div.style.position = 'absolute';
div.style.border = "1px solid black";
div.style.width = '300px';
div.style.height = '100px';

// Drag and Drop処理
let isDragging = false;
let offsetX, offsetY;

div.addEventListener('mousedown', (e) => {
  isDragging = true;
  offsetX = e.clientX - div.getBoundingClientRect().left;
  offsetY = e.clientY - div.getBoundingClientRect().top;
  document.addEventListener('mousemove', move);
  document.addEventListener('mouseup', () => {
    isDragging = false;
    document.removeEventListener('mousemove', move);
    document.addEventListener('mouseup', stopDragging);
  });
});

function move(e) {
  if (isDragging) {
    div.style.left = (e.clientX - offsetX) + 'px';
    div.style.top = (e.clientY - offsetY) + 'px';
  }
}
  const stopDragging = () => {
    isDragging = false;
    document.removeEventListener('mousemove', move);
    document.removeEventListener('mouseup', stopDragging);
  };
document.body.appendChild(div);

Dragするものされるもの

function createDragDivSelf(x, y, w, h, text) {
  const div = document.createElement('div');
  div.style.position = 'absolute';
  div.style.border = "1px solid black";
  div.style.left = x + 'px';
  div.style.top = y + 'px';
  div.style.width = w + 'px';
  div.style.height = h + 'px';
  div.innerHTML = text;

  createDragEventListener(div)

  return div;
}

function createDragDiv(target, x, y, w, h, text) {
  const div = document.createElement('div');
  div.style.position = 'absolute';
  div.style.border = "1px solid black";
  div.style.left = x + 'px';
  div.style.top = y + 'px';
  div.style.width = w + 'px';
  div.style.height = h + 'px';
  div.innerHTML = text;

  createDragEventListener(div, target);

  return div;
}

function createDragEventListener(div, target) {

  // Drag and Drop処理
  let isDragging = false;
  let offsetX, offsetY;
  let offsetTX, offsetTY;
  
  div.addEventListener('mousedown', (e) => {
    isDragging = true;
    offsetX = e.clientX - div.getBoundingClientRect().left;
    offsetY = e.clientY - div.getBoundingClientRect().top;
    if (target) {
      offsetTX = e.clientX - target.getBoundingClientRect().left;
      offsetTY = e.clientY - target.getBoundingClientRect().top;    
    }

    document.addEventListener('mousemove', move);
    document.addEventListener('mouseup', stopDragging);
  });

  function move(e) {
    if (isDragging) {
      div.style.left = (e.clientX - offsetX) + 'px';
      div.style.top = (e.clientY - offsetY) + 'px';
      if (target) {
        target.style.left = (e.clientX - offsetTX) + 'px';
        target.style.top = (e.clientY - offsetTY) + 'px';
      }
    }
  }
  const stopDragging = () => {
    isDragging = false;
    document.removeEventListener('mousemove', move);
    document.removeEventListener('mouseup', stopDragging);
  };
}

//自分自身をdrag
const divSelf = createDragDivSelf(10,10,100,100,"hello");

//dragする側とされる側
const dragged = document.createElement('div');
dragged.innerHTML = "dragged";
dragged.style.position = 'absolute';
dragged.style.border = "1px solid black";
dragged.style.left = 300 + 'px';
dragged.style.top = 100 + 'px';
dragged.style.width = 100 + 'px';
dragged.style.height = 100 + 'px';
const dragger = createDragDiv(dragged, 300,10,20,20,"dragger")


document.body.appendChild(divSelf);
document.body.appendChild(dragger);
document.body.appendChild(dragged);

基本的な考え
ある要素を平行移動する
それに引きずられるものがあればそれも平行移動する
引きずられるものがなければある要素のみが動く(自分自身)

拡張すればtargetを複数形にできる。

IDやclassNameでDOM要素探してlogで表示

function createLogView(x,y,w,h) {
  const div = document.createElement('div');
  div.style.display = 'flex';
  div.style.flexDirection = "column";
  div.style.position = 'absolute';
  div.style.left = x + 'px';
  div.style.top = y + 'px';
  div.style.width = w + 'px';
  div.style.height = h + 'px';  

  // Input for ID
  const targetDOMInputByID = document.createElement('input');
  targetDOMInputByID.placeholder = "Enter ID";
  div.appendChild(targetDOMInputByID);

  // Button to get element by ID
  const btnGetByID = document.createElement('button');
  btnGetByID.textContent = "Get by ID";
  div.appendChild(btnGetByID);

  // Input for Class
  const targetDOMInputByClass = document.createElement('input');
  targetDOMInputByClass.placeholder = "Enter Class";
  div.appendChild(targetDOMInputByClass);

  // Button to get element by Class
  const btnGetByClass = document.createElement('button');
  btnGetByClass.textContent = "Get by Class";
  div.appendChild(btnGetByClass);

  let targetDOM;

  btnGetByID.addEventListener('click', function () {
    const idValue = targetDOMInputByID.value;
    targetDOM = document.getElementById(idValue);
    console.log(targetDOM);
  });

  btnGetByClass.addEventListener('click', function () {
    const classValue = targetDOMInputByClass.value;
    targetDOM = document.getElementsByClassName(classValue)[0]; // Only the first element with this class
    console.log(targetDOM);
  });

  return div;
}

const viewer = createLogView(200,200,100,100);
document.body.appendChild(viewer);

Canvas+Scrollbar

初手

canvas要素包む。
行は減るがパラメータ数は変わっておらず。


export function CreateCanvas(x, y, width, height, id) {
    const canvas = document.createElement('canvas');
    canvas.id = id;
    canvas.style.border = "1px solid black";

    canvas.x = x;
    canvas.y = y;
    canvas.width = width;//canvas要素はwidth,heightを持つので+"px"は不要
    canvas.height = height;

    return canvas;
}

2手目

+ScrollBar
つくったcanvasを包む。
座標の移植とoverflowパラメータの操作。

export function CreateCanvasContainer(canvas) {
    const container = document.createElement('div');
    container.id = canvas.id + "Container";
    //container.style.position = "absolute";
    container.style.border = "1px solid black";

    container.style.top = canvas.y + "px";
    container.style.left = canvas.x + "px";
    container.style.width = canvas.width + "px";//div要素はwidth,heightを持たないので+"px"が必要
    container.style.height = canvas.height + "px";

    //canvas側は座標を(0,0)に
    canvas.style.top = 0 + 'px';
    canvas.style.left = 0 + 'px';

    container.style.overflow = "scroll";

    container.appendChild(canvas);
    //parent.appendChild(container);

    return container;
}

関数内部でcanvasも作ってしまうパターン。

export function CreateCanvasContainer(x, y, width, height, id) 

Flexboxに差し込むことなどを考えるとappenChildも内部でやってしまうパターン。

export function CreateCanvasContainer(parent, x, y, width, height, id) 

レイヤー選択タブの元

//let x,y,w,h=100;//これだとhのみに100がはいる
let x = 100, y = 100, w = 100, h = 100;

const mainFrame = document.createElement('div');
mainFrame.id = "main_frame";
mainFrame.className = "MainFrame"
mainFrame.style.display = 'flex';
mainFrame.style.flexDirection = "column";
mainFrame.style.position = 'absolute';
mainFrame.style.border = "1px solid black";
mainFrame.style.left = x + 'px';
mainFrame.style.top = y + 'px';
// div.style.width = w + 'px';
// div.style.height = h + 'px';
//mainFrame.innerHTML = "hello";

document.body.appendChild(mainFrame);


const menubar = document.createElement('div');
menubar.id = "menubar";
menubar.className = "Menubar";
menubar.style.display = 'flex';
menubar.style.flexDirection = "row";
menubar.style.border = "1px solid black";
menubar.innerHTML = "menu";
mainFrame.appendChild(menubar);


let tabCount = 0;
const tabWidth = 300;
const tabHeight = 50;

let currentTab = null;

const upButton = document.createElement('button');
upButton.textContent = "up";
menubar.appendChild(upButton);

const downButton = document.createElement('button');
downButton.textContent = "down";
menubar.appendChild(downButton);

upButton.addEventListener('click', function () {
  if (currentTab && currentTab.previousElementSibling) {
    scrollContainer.insertBefore(currentTab, currentTab.previousElementSibling);
  }
});

downButton.addEventListener('click', function () {
  if (currentTab && currentTab.nextElementSibling) {
    scrollContainer.insertBefore(currentTab.nextElementSibling, currentTab);
  }
});

const addButton = document.createElement('button');
addButton.textContent = "add";
menubar.appendChild(addButton);
addButton.addEventListener('click', function () {
  const tab = document.createElement('div');
  tab.className = "Tab";
  tab.style.display = 'flex';
  tab.style.flexDirection = "row";
  tab.style.border = "1px solid black";
  tab.style.width = tabWidth + "px";
  tab.style.height = tabHeight + "px";
  tab.innerHTML = `tab ${tabCount}`;//バックスティックを使う
  scrollContainer.appendChild(tab);
  tabCount++;
});

const scrollContainer = document.createElement('div');
scrollContainer.id = "scroll_container";
scrollContainer.className = "ScrollContainer";
scrollContainer.style.overflow = "scroll";
scrollContainer.style.display = 'flex';
scrollContainer.style.flexDirection = "column";
scrollContainer.style.border = "1px solid black";
scrollContainer.innerHTML = "hello";
mainFrame.appendChild(scrollContainer);

scrollContainer.addEventListener('click', function (e) {
  const tab = e.target.closest('div');
  if (tab&&tab.className==="Tab") {
    
    if(currentTab){
      currentTab.style.background = "";
    }
    currentTab = tab;    
    currentTab.style.background = "rgba(0, 0, 255, 0.5)";
    //console.log(tab.innerHTML);
  }
});

const statusbar = document.createElement('div');
menubar.id = "statusbar";
menubar.className = "Statusbar";
statusbar.style.display = 'flex';
statusbar.style.flexDirection = "row";
statusbar.style.border = "1px solid black";
statusbar.innerHTML = "status";
mainFrame.appendChild(statusbar);

scrollContainer(Tabの親)のconst tab = e.target.closest('div');と
if (tab&&tab.className==="Tab")で
並んでるタブからクリックされたものを検出。

畳めるdiv要素



export function MakeFoldingbox(parent, w, h) {

    let foldingbar_h = 10;

    // フレックスコンテナを生成
    const flexContainer = document.createElement('div');
    flexContainer.id = 'Foldingbox';
    flexContainer.style.position = "absolute";

    flexContainer.style.display = "flex";
    flexContainer.style.flexDirection = "column";
    flexContainer.style.width = w + "px";
    //container.style.height = canvas.height + "px";    

    parent.appendChild(flexContainer);

    const scrollContainer = document.createElement('div');
    scrollContainer.id = "ScrollContainer";
    scrollContainer.style.border = "1px solid black";
    scrollContainer.style.overflow = "scroll";
    scrollContainer.style.width = w + "px";
    scrollContainer.style.height = h-foldingbar_h + "px";        
    flexContainer.appendChild(scrollContainer);

    //これは後から格納する
    // const innerDiv = document.createElement('div');
    // innerDiv.id = canvas.id + "innerDiv";
    // innerDiv.style.border = "1px solid black";
    // innerDiv.style.width = w + "px";
    // innerDiv.style.height = h-foldingbar_h + "px";        
    // scrollContainer.appendChild(innerDiv);    

    const foldingbar = document.createElement('div');
    foldingbar.id = "Foldingbar";
    foldingbar.style.border = "1px solid black";
    foldingbar.style.width = w + "px";
    foldingbar.style.height = foldingbar_h + "px";    
    flexContainer.appendChild(foldingbar);    


    let isDragging = false;
    let startY;
    foldingbar.addEventListener("mousedown", (e) => {
        isDragging = true;
        startY = e.clientY;
    });

    window.addEventListener("mousemove", (e) => {
        if (isDragging) {
            const deltaY = e.clientY - startY;

            const newHeight = parseInt(scrollContainer.style.height) + deltaY;
            if (newHeight > 0 && foldingbar.offsetTop >= scrollContainer.offsetTop) {
                scrollContainer.style.height = newHeight + "px";
                foldingbar.style.top = (foldingbar.offsetTop + deltaY) + "px";
            }
            startY = e.clientY;
        }
    });

    window.addEventListener("mouseup", () => {
        isDragging = false;
    });

    return flexContainer;
}



Flexbox(Col)にFlexbox(Row)を継いでいく

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Canvas Grids</title>
</head>
<body>
    <script src="main.js"></script>
</body>
</html>

main.js





	let x = 100;
	let y = 100;

	class InputManager {
	    constructor() {
	        this.isDragging = true;
	        this.prevX = 0;
	        this.prevY = 0;
	    }
	}

	const inputManager = new InputManager();


    // フレックスコンテナを生成
    const flexContainerCol = document.createElement('div');
    flexContainerCol.id = 'mainFrame';
    flexContainerCol.style.position = "absolute";

    flexContainerCol.style.display = "flex";
    flexContainerCol.style.flexDirection = "column";
    //flexContainerCol.style.alignItems = "center";
    // flexContainerCol.style.zIndex = "100"; //mainFrameより優先
    flexContainerCol.style.left = x + "px";
    flexContainerCol.style.top = y + "px";
    flexContainerCol.style.width = '300px';
    flexContainerCol.style.height = '500px';
	flexContainerCol.style.border = '1px solid black';

    document.body.appendChild(flexContainerCol);

    /////////////////////////////////////////////////////////////////////////////

    const flexContainerRow = document.createElement('div');
    flexContainerRow.style.width = '300px';
    flexContainerRow.style.height = '50px';
    flexContainerRow.style.border = '1px solid black';
    flexContainerRow.style.display = 'flex';
    flexContainerRow.style.flexDirection = "row";
    //this.flexContainerRow.style.justifyContent = 'center';
    //this.flexContainerRow.style.alignItems = 'center';
    flexContainerCol.appendChild(flexContainerRow);

    /////////////////////////////////////////////////////////////////////////////

    // ドラッグ対象のdivを作成
    const draggableDiv = document.createElement('div');
    draggableDiv.textContent = "ドラッグして移動";
    draggableDiv.style.border = "1px solid black";
    //draggableDiv.style.padding = '10px';
    //draggableDiv.style.background = 'lightgray';
    draggableDiv.style.cursor = 'pointer';
    flexContainerRow.appendChild(draggableDiv);

    // mousedownイベント
    draggableDiv.addEventListener("mousedown", function (event) {
        inputManager.isDragging = true;
        inputManager.prevX = event.clientX;
        inputManager.prevY = event.clientY;
    });

    window.addEventListener("mousemove", function (event) {
        if (inputManager.isDragging) {
            

            const dx = event.clientX - inputManager.prevX;
            const dy = event.clientY - inputManager.prevY;

            if (mainFrame.style.display === "none") {
                // menubarの現在の位置を取得
                const currentTop = parseInt(flexContainerRow.style.top || 0, 10);
                const currentLeft = parseInt(flexContainerRow.style.left || 0, 10);

                // 新しい位置を設定
                flexContainerRow.style.top = `${currentTop + dy}px`;
                flexContainerRow.style.left = `${currentLeft + dx}px`;
            } else {
                // mainFrameの現在の位置を取得
                const currentTop = parseInt(flexContainerCol.style.top || 0, 10);
                const currentLeft = parseInt(flexContainerCol.style.left || 0, 10);

                // 新しい位置を設定
                flexContainerCol.style.top = `${currentTop + dy}px`;
                flexContainerCol.style.left = `${currentLeft + dx}px`;
            }

            inputManager.prevX = event.clientX;
            inputManager.prevY = event.clientY;
        }
    });

    // mouseupイベント
    window.addEventListener("mouseup", function () {
        inputManager.isDragging = false;
    });


関数の分離

親の親をドラッグできる、という能力をdiv要素に持たせたい。
そのため、div要素のparentNodeを2回呼びだして親の親を辿るようにした。
また、親をドラッグできる、という能力も与えたい。
結果関数として分離した。

targetをドラッグしたい、といった抽象的な要求はclass化するなりグローバススコープに変数を確保する必要があると思われる。

やること:クロージャを試す。



	let x = 100;
	let y = 100;

	class InputManager {
	    constructor() {
	        this.isDragging = true;
	        this.prevX = 0;
	        this.prevY = 0;
	    }
	}

	const inputManager = new InputManager();


    // フレックスコンテナを生成
    const flexContainerCol = document.createElement('div');
    flexContainerCol.id = 'mainFrame';
    flexContainerCol.style.position = "absolute";

    flexContainerCol.style.display = "flex";
    flexContainerCol.style.flexDirection = "column";
    //flexContainerCol.style.alignItems = "center";
    // flexContainerCol.style.zIndex = "100"; //mainFrameより優先
    flexContainerCol.style.left = x + "px";
    flexContainerCol.style.top = y + "px";
    flexContainerCol.style.width = '300px';
    flexContainerCol.style.height = '500px';
	flexContainerCol.style.border = '1px solid black';

    document.body.appendChild(flexContainerCol);

    /////////////////////////////////////////////////////////////////////////////

    const flexContainerRow = document.createElement('div');
	flexContainerRow.style.position = "absolute";
    flexContainerRow.style.width = '300px';
    flexContainerRow.style.height = '50px';
    flexContainerRow.style.border = '1px solid black';
    flexContainerRow.style.display = 'flex';
    flexContainerRow.style.flexDirection = "row";
    //this.flexContainerRow.style.justifyContent = 'center';
    //this.flexContainerRow.style.alignItems = 'center';
    
    flexContainerCol.appendChild(flexContainerRow);
	//document.body.appendChild(flexContainerRow);

    /////////////////////////////////////////////////////////////////////////////

    // ドラッグ対象のdivを作成
    const draggableDiv = document.createElement('div');
    draggableDiv.textContent = "ドラッグして移動";
    draggableDiv.style.border = "1px solid black";
    //draggableDiv.style.padding = '10px';
    //draggableDiv.style.background = 'lightgray';
    draggableDiv.style.cursor = 'pointer';
    flexContainerRow.appendChild(draggableDiv);

    // mousedownイベント
    draggableDiv.addEventListener("mousedown", function (event) {
        inputManager.isDragging = true;
        inputManager.prevX = event.clientX;
        inputManager.prevY = event.clientY;
    });

	
	window.addEventListener("mousemove", parentParentDrag);


    // mouseupイベント
    window.addEventListener("mouseup", function () {
        inputManager.isDragging = false;
    });



function parentParentDrag(event) {
    if (inputManager.isDragging) {
        const dx = event.clientX - inputManager.prevX;
        const dy = event.clientY - inputManager.prevY;

        // draggableDivの親の親を取得
        const parentOfParent = draggableDiv.parentNode.parentNode;

        // 親の親の現在の位置を取得
        const currentTop = parseInt(parentOfParent.style.top || 0, 10);
        const currentLeft = parseInt(parentOfParent.style.left || 0, 10);

        // 新しい位置を設定
        parentOfParent.style.top = `${currentTop + dy}px`;
        parentOfParent.style.left = `${currentLeft + dx}px`;

        inputManager.prevX = event.clientX;
        inputManager.prevY = event.clientY;
    }
}

function parentDrag(event) {
    if (inputManager.isDragging) {
        const dx = event.clientX - inputManager.prevX;
        const dy = event.clientY - inputManager.prevY;

        // draggableDivの親の親を取得
        const parent = draggableDiv.parentNode;

        // 親の親の現在の位置を取得
        const currentTop = parseInt(parent.style.top || 0, 10);
        const currentLeft = parseInt(parent.style.left || 0, 10);

        // 新しい位置を設定
        parent.style.top = `${currentTop + dy}px`;
        parent.style.left = `${currentLeft + dx}px`;

        inputManager.prevX = event.clientX;
        inputManager.prevY = event.clientY;
    }
}

[Purge]ParentのParentからParentを切り離す



	let x = 100;
	let y = 100;

	class InputManager {
	    constructor() {
	        this.isDragging = true;
	        this.prevX = 0;
	        this.prevY = 0;
	    }
	}

	const inputManager = new InputManager();


    // フレックスコンテナを生成
    const flexContainerCol = document.createElement('div');
    flexContainerCol.id = 'mainFrame';
    flexContainerCol.style.position = "absolute";

    flexContainerCol.style.display = "flex";
    flexContainerCol.style.flexDirection = "column";
    //flexContainerCol.style.alignItems = "center";
    // flexContainerCol.style.zIndex = "100"; //mainFrameより優先
    flexContainerCol.style.left = x + "px";
    flexContainerCol.style.top = y + "px";
    flexContainerCol.style.width = '300px';
    flexContainerCol.style.height = '500px';
	flexContainerCol.style.border = '1px solid black';

    document.body.appendChild(flexContainerCol);

    /////////////////////////////////////////////////////////////////////////////

    const flexContainerRow = document.createElement('div');
	flexContainerRow.style.position = "absolute";
    flexContainerRow.style.width = '300px';
    flexContainerRow.style.height = '50px';
    flexContainerRow.style.border = '1px solid black';
    flexContainerRow.style.display = 'flex';
    flexContainerRow.style.flexDirection = "row";
    //this.flexContainerRow.style.justifyContent = 'center';
    //this.flexContainerRow.style.alignItems = 'center';
    
    flexContainerCol.appendChild(flexContainerRow);
	//document.body.appendChild(flexContainerRow);

    /////////////////////////////////////////////////////////////////////////////

    // ドラッグ対象のdivを作成
    const draggableDiv = document.createElement('div');
    draggableDiv.textContent = "ドラッグして移動";
    draggableDiv.style.border = "1px solid black";
    //draggableDiv.style.padding = '10px';
    //draggableDiv.style.background = 'lightgray';
    draggableDiv.style.cursor = 'pointer';
    flexContainerRow.appendChild(draggableDiv);

    // mousedownイベント
    draggableDiv.addEventListener("mousedown", function (event) {
        inputManager.isDragging = true;
        inputManager.prevX = event.clientX;
        inputManager.prevY = event.clientY;
    });

	
	window.addEventListener("mousemove", parentParentDrag);


    // mouseupイベント
    window.addEventListener("mouseup", function () {
        inputManager.isDragging = false;
    });


	// 新しいdiv要素を作成
	const newDiv = document.createElement('div');
	newDiv.textContent = "Purge";
	newDiv.style.border = "1px solid red";
	//newDiv.style.margin = "10px";
	newDiv.style.cursor = 'pointer';
	flexContainerRow.appendChild(newDiv);

	newDiv.addEventListener("mousedown", function(event) {
	    // 親要素を親の親から削除
	    const parent = this.parentNode;
	    const parentOfParent = parent.parentNode;

		let x = getAbsolutePositionX(parent);
		let y = getAbsolutePositionY(parent);

		parent.style.left = x + "px";
		parent.style.top = y + "px";
		
	    parentOfParent.removeChild(parent);
		document.body.appendChild(parent);

	    // parentParentDragからparentDragに切り替え
	    window.removeEventListener("mousemove", parentParentDrag);
	    window.addEventListener("mousemove", parentDrag);

	    // ドラッグを開始
	    inputManager.isDragging = true;
	    inputManager.prevX = event.clientX;
	    inputManager.prevY = event.clientY;
	});



function parentParentDrag(event) {
    if (inputManager.isDragging) {
        const dx = event.clientX - inputManager.prevX;
        const dy = event.clientY - inputManager.prevY;

        // draggableDivの親の親を取得
        const parentOfParent = draggableDiv.parentNode.parentNode;

        // 親の親の現在の位置を取得
        const currentTop = parseInt(parentOfParent.style.top || 0, 10);
        const currentLeft = parseInt(parentOfParent.style.left || 0, 10);

        // 新しい位置を設定
        parentOfParent.style.top = `${currentTop + dy}px`;
        parentOfParent.style.left = `${currentLeft + dx}px`;

        inputManager.prevX = event.clientX;
        inputManager.prevY = event.clientY;
    }
}

function parentDrag(event) {
    if (inputManager.isDragging) {
        const dx = event.clientX - inputManager.prevX;
        const dy = event.clientY - inputManager.prevY;

        // draggableDivの親の親を取得
        const parent = draggableDiv.parentNode;

        // 親の親の現在の位置を取得
        const currentTop = parseInt(parent.style.top || 0, 10);
        const currentLeft = parseInt(parent.style.left || 0, 10);

        // 新しい位置を設定
        parent.style.top = `${currentTop + dy}px`;
        parent.style.left = `${currentLeft + dx}px`;

        inputManager.prevX = event.clientX;
        inputManager.prevY = event.clientY;
    }
}


    function getAbsolutePositionX(element) {
        let xPosition = 0;
        while (element) {
            xPosition += (element.offsetLeft - element.scrollLeft + element.clientLeft);
            element = element.offsetParent;
        }
        return xPosition;
    }
    function getAbsolutePositionY(element) {
        let yPosition = 0;
        while (element) {
            yPosition += (element.offsetTop - element.scrollTop + element.clientTop);
            element = element.offsetParent;
        }
        return yPosition;
    }

getBoundingClientRectを使う場合

	let x = 100;
	let y = 100;

	class InputManager {
	    constructor() {
	        this.isDragging = true;
	        this.prevX = 0;
	        this.prevY = 0;
	    }
	}

	const inputManager = new InputManager();


    // フレックスコンテナを生成
    const flexContainerCol = document.createElement('div');
    flexContainerCol.id = 'mainFrame';
    flexContainerCol.style.position = "absolute";

    flexContainerCol.style.display = "flex";
    flexContainerCol.style.flexDirection = "column";
    //flexContainerCol.style.alignItems = "center";
    // flexContainerCol.style.zIndex = "100"; //mainFrameより優先
    flexContainerCol.style.left = x + "px";
    flexContainerCol.style.top = y + "px";
    flexContainerCol.style.width = '300px';
    flexContainerCol.style.height = '500px';
	flexContainerCol.style.border = '1px solid black';

    document.body.appendChild(flexContainerCol);

    /////////////////////////////////////////////////////////////////////////////

    const flexContainerRow = document.createElement('div');
	flexContainerRow.style.position = "absolute";
    flexContainerRow.style.width = '300px';
    flexContainerRow.style.height = '50px';
    flexContainerRow.style.border = '1px solid black';
    flexContainerRow.style.display = 'flex';
    flexContainerRow.style.flexDirection = "row";
    //this.flexContainerRow.style.justifyContent = 'center';
    //this.flexContainerRow.style.alignItems = 'center';
    
    flexContainerCol.appendChild(flexContainerRow);
	//document.body.appendChild(flexContainerRow);

    /////////////////////////////////////////////////////////////////////////////

    // ドラッグ対象のdivを作成
    const draggableDiv = document.createElement('div');
    draggableDiv.textContent = "ドラッグして移動";
    draggableDiv.style.border = "1px solid black";
    //draggableDiv.style.padding = '10px';
    //draggableDiv.style.background = 'lightgray';
    draggableDiv.style.cursor = 'pointer';
    flexContainerRow.appendChild(draggableDiv);

    // mousedownイベント
    draggableDiv.addEventListener("mousedown", function (event) {
        inputManager.isDragging = true;
        inputManager.prevX = event.clientX;
        inputManager.prevY = event.clientY;
    });

	
	window.addEventListener("mousemove", parentParentDrag);


    // mouseupイベント
    window.addEventListener("mouseup", function () {
        inputManager.isDragging = false;
    });


	// 新しいdiv要素を作成
	const newDiv = document.createElement('div');
	newDiv.textContent = "Purge";
	newDiv.style.border = "1px solid red";
	//newDiv.style.margin = "10px";
	newDiv.style.cursor = 'pointer';
	flexContainerRow.appendChild(newDiv);

newDiv.addEventListener("mousedown", function(event) {
    // 親要素を親の親から削除
    const parent = this.parentNode;
    const parentOfParent = parent.parentNode;

    // getBoundingClientRectを使用して要素の位置を取得
    const rect = parent.getBoundingClientRect();
    let x = rect.left;
    let y = rect.top;
    parent.style.left = x + "px";
    parent.style.top = y + "px";
    
	// 要素の位置とマウスの位置のオフセットを計算する場合
    //const offsetX = event.clientX - x;
    //const offsetY = event.clientY - y;
    //parent.style.left = (x + offsetX) + "px";
    //parent.style.top = (y + offsetY) + "px";

    parentOfParent.removeChild(parent);
    document.body.appendChild(parent);

    // parentParentDragからparentDragに切り替え
    window.removeEventListener("mousemove", parentParentDrag);
    window.addEventListener("mousemove", parentDrag);

    // ドラッグを開始
    inputManager.isDragging = true;
    inputManager.prevX = event.clientX;
    inputManager.prevY = event.clientY;
});



function parentParentDrag(event) {
    if (inputManager.isDragging) {
        const dx = event.clientX - inputManager.prevX;
        const dy = event.clientY - inputManager.prevY;

        // draggableDivの親の親を取得
        const parentOfParent = draggableDiv.parentNode.parentNode;

        // 親の親の現在の位置を取得
        const currentTop = parseInt(parentOfParent.style.top || 0, 10);
        const currentLeft = parseInt(parentOfParent.style.left || 0, 10);

        // 新しい位置を設定
        parentOfParent.style.top = `${currentTop + dy}px`;
        parentOfParent.style.left = `${currentLeft + dx}px`;

        inputManager.prevX = event.clientX;
        inputManager.prevY = event.clientY;
    }
}

function parentDrag(event) {
    if (inputManager.isDragging) {
        const dx = event.clientX - inputManager.prevX;
        const dy = event.clientY - inputManager.prevY;

        // draggableDivの親の親を取得
        const parent = draggableDiv.parentNode;

        // 親の親の現在の位置を取得
        const currentTop = parseInt(parent.style.top || 0, 10);
        const currentLeft = parseInt(parent.style.left || 0, 10);

        // 新しい位置を設定
        parent.style.top = `${currentTop + dy}px`;
        parent.style.left = `${currentLeft + dx}px`;

        inputManager.prevX = event.clientX;
        inputManager.prevY = event.clientY;
    }
}

[Dock]Purgeしてから元に戻す

ただし現状の実装だと切り離した瞬間に元に戻ってしまうエリアがある。



	let x = 100;
	let y = 100;

	class InputManager {
	    constructor() {
	        this.isDragging = true;
	        this.prevX = 0;
	        this.prevY = 0;
	    }
	}

	const inputManager = new InputManager();


    // フレックスコンテナを生成
    const flexContainerCol = document.createElement('div');
    flexContainerCol.id = 'mainFrame';
    flexContainerCol.style.position = "absolute";

    flexContainerCol.style.display = "flex";
    flexContainerCol.style.flexDirection = "column";
    //flexContainerCol.style.alignItems = "center";
    // flexContainerCol.style.zIndex = "100"; //mainFrameより優先
    flexContainerCol.style.left = x + "px";
    flexContainerCol.style.top = y + "px";
    flexContainerCol.style.width = '300px';
    flexContainerCol.style.height = '500px';
	flexContainerCol.style.border = '1px solid black';

    document.body.appendChild(flexContainerCol);

    /////////////////////////////////////////////////////////////////////////////

    const flexContainerRow = document.createElement('div');
	flexContainerRow.style.position = "absolute";
    flexContainerRow.style.width = '300px';
    flexContainerRow.style.height = '50px';
    flexContainerRow.style.border = '1px solid black';
    flexContainerRow.style.display = 'flex';
    flexContainerRow.style.flexDirection = "row";
    //this.flexContainerRow.style.justifyContent = 'center';
    //this.flexContainerRow.style.alignItems = 'center';
    
    flexContainerCol.appendChild(flexContainerRow);
	//document.body.appendChild(flexContainerRow);

    /////////////////////////////////////////////////////////////////////////////

    // ドラッグ対象のdivを作成
    const draggableDiv = document.createElement('div');
    draggableDiv.textContent = "ドラッグして移動";
    draggableDiv.style.border = "1px solid black";
    //draggableDiv.style.padding = '10px';
    //draggableDiv.style.background = 'lightgray';
    draggableDiv.style.cursor = 'pointer';
    flexContainerRow.appendChild(draggableDiv);

    // mousedownイベント
    draggableDiv.addEventListener("mousedown", function (event) {
        inputManager.isDragging = true;
        inputManager.prevX = event.clientX;
        inputManager.prevY = event.clientY;
    });

	
	window.addEventListener("mousemove", parentParentDrag);


    // mouseupイベント
    window.addEventListener("mouseup", function () {
        inputManager.isDragging = false;
    });


	// 新しいdiv要素を作成
	const newDiv = document.createElement('div');
	newDiv.textContent = "Purge";
	newDiv.style.border = "1px solid red";
	//newDiv.style.margin = "10px";
	newDiv.style.cursor = 'pointer';
	flexContainerRow.appendChild(newDiv);

newDiv.addEventListener("mousedown", function(event) {
    // 親要素を親の親から削除
    const parent = this.parentNode;
    const parentOfParent = parent.parentNode;

    // getBoundingClientRectを使用して要素の位置を取得
    const rect = parent.getBoundingClientRect();
    let x = rect.left;
    let y = rect.top;
    parent.style.left = x + "px";
    parent.style.top = y + "px";
    
	// 要素の位置とマウスの位置のオフセットを計算する場合
    //const offsetX = event.clientX - x;
    //const offsetY = event.clientY - y;
    //parent.style.left = (x + offsetX) + "px";
    //parent.style.top = (y + offsetY) + "px";

    parentOfParent.removeChild(parent);
    document.body.appendChild(parent);

    // parentParentDragからparentDragに切り替え
    window.removeEventListener("mousemove", parentParentDrag);
    window.addEventListener("mousemove", parentDrag);

    // ドラッグを開始
    inputManager.isDragging = true;
    inputManager.prevX = event.clientX;
    inputManager.prevY = event.clientY;
});



function parentParentDrag(event) {
    if (inputManager.isDragging) {
        const dx = event.clientX - inputManager.prevX;
        const dy = event.clientY - inputManager.prevY;

        // draggableDivの親の親を取得
        const parentOfParent = draggableDiv.parentNode.parentNode;

        // 親の親の現在の位置を取得
        const currentTop = parseInt(parentOfParent.style.top || 0, 10);
        const currentLeft = parseInt(parentOfParent.style.left || 0, 10);

        // 新しい位置を設定
        parentOfParent.style.top = `${currentTop + dy}px`;
        parentOfParent.style.left = `${currentLeft + dx}px`;

        inputManager.prevX = event.clientX;
        inputManager.prevY = event.clientY;
    }
}

function parentDrag(event) {
    if (inputManager.isDragging) {
        const dx = event.clientX - inputManager.prevX;
        const dy = event.clientY - inputManager.prevY;

        // draggableDivの親を取得
        const parent = draggableDiv.parentNode;

        // 親の現在の位置を取得
        const currentTop = parseInt(parent.style.top || 0, 10);
        const currentLeft = parseInt(parent.style.left || 0, 10);

        // 新しい位置を設定
        parent.style.top = `${currentTop + dy}px`;
        parent.style.left = `${currentLeft + dx}px`;

        inputManager.prevX = event.clientX;
        inputManager.prevY = event.clientY;

        // ここから条件をチェックして、指定の操作を行います
        const flexContainerColRect = flexContainerCol.getBoundingClientRect();
        const parentRect = parent.getBoundingClientRect();


        if (flexContainerColRect.left < parentRect.left && parentRect.left < flexContainerColRect.left + 20) { 			
			if(flexContainerColRect.bottom > parentRect.top && parentRect.top > flexContainerColRect.top) {
            
			parent.style.top = 0 + "px";
			parent.style.left = 0 + "px";

			// parentをdocument.bodyからremoveChild
            document.body.removeChild(parent);
            // parentをflexContainerColにappendChild
            flexContainerCol.appendChild(parent);
            
            // parentParentDragに戻す
            window.removeEventListener("mousemove", parentDrag);
            window.addEventListener("mousemove", parentParentDrag);
			}
        }
    }
}

div要素のルートまで辿る

ほんとのルートはdocument.bodyになるので、document.bodyの直接の子供まで辿る。

function findConvenientRoot(element) {
    while (element && element.parentNode !== document.body) {
        element = element.parentNode;
    }
    return element;
}

// 使用例
let div = document.querySelector('#someDiv'); // 任意のdiv要素を取得
let root = findConvenientRoot(div);

console.log(root); // divからdocument.bodyの直接の子要素への参照

この記事が気に入ったらサポートをしてみませんか?