DOM要素定石探訪②


displayとvisibility

style.display

  1. block:

    • 要素はブロックレベル要素として表示されます。これは要素が新しい行の上で始まり、フル幅を取ることを意味します。例: <div>, <p>

  2. inline:

    • 要素はインライン要素として表示され、テキストや他のインライン要素と同じ行上に表示されます。ボックスの幅は内容に合わせて調整されます。例: <span>, <a>

  3. inline-block:

    • 要素はインライン要素として表示されますが、ブロックのようなプロパティを持ちます。これにより、インライン要素の中にマージンやパディングを持つボックスを持つことができます。

  4. none:

    • 要素は完全に非表示になり、レイアウト上でスペースも占有しません。

  5. flex:

    • 要素はフレキシブルボックスとして表示され、その子要素は新しいレイアウトコンテキスト内で配置されます。

  6. grid:

    • 要素はグリッドコンテナとして表示され、その子要素はグリッドアイテムとして新しいレイアウトコンテキスト内で配置されます。

など、他にも多数の値がありますが、これらは最も一般的なものです。

style.visibility

  1. visible:

    • 要素は普通に表示されます(これがデフォルトの状態です)。

  2. hidden:

    • 要素は非表示になりますが、レイアウト上でスペースを占有し続けます。

  3. collapse:

    • 主にテーブルの行や列に使用され、指定した行や列を非表示にします。しかし、通常の要素ではhiddenのように動作します。

これらのプロパティと値の理解は、ウェブページのレイアウトや表示を調整する際に非常に重要です。


Flexboxスケールと最小化

width, height

Flexboxの子要素はw, hを設定せず、Flexboxに任す。
でなければFlexboxの拡大縮小時に子要素の設定が邪魔になる。
基本的にはflexGrowなどで対応する。
もちろんFlexboxの大きさを任意に固定したければ子要素にw, hを設定すればよい。

display, visibility

生成したDOMを見えなくするにはdisplay='none'かvisibility='hidden'である。Flexboxはdisplay='flex'であるため、noneにして消したならdisplay=''では回復しない。

座標は
style.top
offsetTop
getBoundingClientRect

などがあるが、バグり倒したなごりでgetBoundingClientRectを用いている。


export function MakeTestDiv(w,h) {

    const flexContainerColumn = document.createElement('div');
    flexContainerColumn.id = 'mainFrame';
    flexContainerColumn.style.width = w + "px";
    flexContainerColumn.style.height = h + "px";
    flexContainerColumn.style.display = "flex";
    flexContainerColumn.style.flexDirection = "column";

    document.body.appendChild(flexContainerColumn);
    flexContainerColumn.style.backgroundColor = 'rgba(255, 99, 71, 0.3)';

    //height決定要素
    const scrollContainer = document.createElement('div');    
    scrollContainer.className  = "ScrollContainer";
    scrollContainer.style.flexGrow = '1';
    scrollContainer.style.border = "1px solid black";
    scrollContainer.style.overflow = "scroll";
    //scrollContainer.style.width = w  + "px";
    //scrollContainer.style.height = h + "px";        
    //scrollContainer.style.width = 300 + "px";
    //scrollContainer.style.height = 300 + "px";        
    flexContainerColumn.appendChild(scrollContainer);

    ////////////////////////////////////////////////////////////////////////////////////////
    //スケール

    const min_w = 50;
    const min_h = 50;

    const scaleDiv = document.createElement('div');
    scaleDiv.textContent = "スケール";
    scaleDiv.style.border = "1px solid black";
    scaleDiv.style.cursor = 'pointer';
    flexContainerColumn.appendChild(scaleDiv);

    let isDragging = false;
    let startY;
    let startX;    

    let startLeft;
    let startTop;
    let startWidth;
    let startHeight;
    
    scaleDiv.addEventListener("mousedown", function (event) {
        isDragging = true;
        startX = event.clientX;
        startY = event.clientY;
    
        let rect = flexContainerColumn.getBoundingClientRect();
    
        startLeft = rect.left;
        startTop = rect.top;
        startWidth = rect.width;
        startHeight = rect.height;
    
        if (startWidth < min_w) {
            startWidth = min_w;
        }
        if (startHeight < min_h) {
            startHeight = min_h;
        }
    });
    
    window.addEventListener("mousemove", function (event) {
        if (isDragging) {
            // 横方向の変更
            const deltaX = event.clientX - startX;
            const newWidth = startWidth + deltaX;    
            // 縦方向の変更
            const deltaY = event.clientY - startY;
            const newHeight = startHeight + deltaY;
    
            let scaleRect = scaleDiv.getBoundingClientRect();
  
            if (newWidth > min_w && scaleRect.left >= startLeft) {
                flexContainerColumn.style.width = newWidth + "px";
            }
            if (newHeight > min_h && scaleRect.top >= startTop) {      
                flexContainerColumn.style.height = newHeight + "px";
            }
        }
    });

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

    const hiddenDiv = document.createElement('div');
    hiddenDiv.textContent = "最小化";
    hiddenDiv.style.border = "1px solid black";
    hiddenDiv.style.cursor = 'pointer';
    flexContainerColumn.appendChild(hiddenDiv);

    //visibility=hiddenの場合
    // hiddenDiv.addEventListener("click", function () {
    //     let totalHeight = 0;
    //     // visibilityスタイルが"hidden"の場合は"visible"にして表示、そうでない場合は"hidden"にして非表示
    //     if (flexContainerColumn.style.visibility === "hidden") {
    //         const menubarRect = hiddenDiv.getBoundingClientRect();
    //         flexContainerColumn.style.top = `${menubarRect.top - document.documentElement.scrollTop}px`;
    //         flexContainerColumn.style.left = `${menubarRect.left - document.documentElement.scrollLeft}px`;
    //         hiddenDiv.style.position = "";
    //         flexContainerColumn.appendChild(hiddenDiv);
    //         flexContainerColumn.style.visibility = "visible";  // visibilityを"visible"に変更
    //     } else {
    //         const targetRect = flexContainerColumn.getBoundingClientRect();
    //         hiddenDiv.style.top = `${targetRect.top - document.documentElement.scrollTop}px`;
    //         hiddenDiv.style.left = `${targetRect.left - document.documentElement.scrollLeft}px`;
    //         hiddenDiv.style.position = "absolute";
    //         flexContainerColumn.removeChild(hiddenDiv);
    //         document.body.appendChild(hiddenDiv);
    //         flexContainerColumn.style.visibility = "hidden";  // visibilityを"hidden"に変更
    //     }//else
    // });    

    //display=noneの場合
    hiddenDiv.addEventListener("click", function () {
        let totalHeight = 0;
        if (flexContainerColumn.style.display === "none") {

            const hiddenDivRect = hiddenDiv.getBoundingClientRect();
            
            flexContainerColumn.style.top = `${hiddenDivRect.top - document.documentElement.scrollTop}px`;
            flexContainerColumn.style.left = `${hiddenDivRect.left - document.documentElement.scrollLeft}px`;
            hiddenDiv.style.position = "";//位置を(0,0)に戻し、flexboxに任せる

            flexContainerColumn.appendChild(hiddenDiv);
            flexContainerColumn.style.display = "flex";
        } else {
            // flexContainerColumnの位置情報を取得してhiddenDivに設定
            //この座標移植処理は最小化DivがflexContainerColumnの最上段にある場合の処理
            //このテストケースでは機能してない

            const flexRect = flexContainerColumn.getBoundingClientRect();
    
            hiddenDiv.style.top = `${flexRect.top - document.documentElement.scrollTop}px`;
            hiddenDiv.style.left = `${flexRect.left - document.documentElement.scrollLeft}px`;
    
            hiddenDiv.style.position = "absolute";
            flexContainerColumn.removeChild(hiddenDiv);
            document.body.appendChild(hiddenDiv);
            flexContainerColumn.style.display = "none";
        }//else
    });
}


Drag, Scale, Minimize

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 type="module" src="main.js"></script>
</body>
</html>

main.js

import * as div from './div.js';

const divs = div.SetupMainFrame(100,100,100,100);
const mainFrame = divs.mainFrame;
const menubar = divs.menubar;
const foldingbox = divs.foldingbox;
const statusbar = divs.statusbar;

document.body.appendChild(mainFrame);

div.js


export function MakeMenubar(target) {

    let isDragging = false;
    let prevX = 0;
    let prevY = 0;
    
    //w, hは内部の要素に任せる
    const menubar = document.createElement('div');
    // menubar.style.width = w +'px';
    // menubar.style.height = h + 'px';
    menubar.style.border = '1px solid black';
    menubar.style.display = 'flex';
    menubar.style.flexDirection = "row";

    //#region
    /////////////////////////////////////////////////////////////////////////////

    // ドラッグ用のdivを作成
    const draggableDiv = document.createElement('div');
    draggableDiv.textContent = "ドラッグして移動";
    draggableDiv.style.border = "1px solid black";
    draggableDiv.style.cursor = 'pointer';
    menubar.appendChild(draggableDiv);

    function handleMouseDown(event) {
        isDragging = true;
        prevX = event.clientX;
        prevY = event.clientY;
    
        // mousemoveイベントリスナを追加
        window.addEventListener("mousemove", handleMouseMove);
        // mouseupイベントリスナを追加
        window.addEventListener("mouseup", handleMouseUp);
    }
    
    function handleMouseMove(event) {
        if (isDragging) {
            if (target.style.display === "none") {
                dragMove(event, menubar);
            } else {
                dragMove(event, target);
            }
            prevX = event.clientX;
            prevY = event.clientY;
        }
    }
    
    function handleMouseUp() {
        isDragging = false;
        // mousemoveとmouseupのイベントリスナを削除
        window.removeEventListener("mousemove", handleMouseMove);
        window.removeEventListener("mouseup", handleMouseUp);
    }
    
    // mousedownイベント
    draggableDiv.addEventListener("mousedown", handleMouseDown);

    function dragMove(event, target) {
    
        const dx = event.clientX - prevX;
        const dy = event.clientY - prevY;
        
        const targetRect = target.getBoundingClientRect();
        const currentTop = targetRect.top - document.documentElement.scrollTop;
        const currentLeft = targetRect.left - document.documentElement.scrollLeft;
    
        // 新しい位置を設定
        target.style.top = `${currentTop + dy}px`;
        target.style.left = `${currentLeft + dx}px`;
    }

    //#endregion

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

    const hiddenDiv = document.createElement('div');
    hiddenDiv.textContent = "最小化";
    hiddenDiv.style.border = "1px solid black";
    hiddenDiv.style.cursor = 'pointer';
    menubar.appendChild(hiddenDiv);
 
    hiddenDiv.addEventListener("click", function () {
        if (target.style.visibility === "hidden") {            
            const menubarRect = menubar.getBoundingClientRect();
            
            target.style.top = `${menubarRect.top - document.documentElement.scrollTop}px`;
            target.style.left = `${menubarRect.left - document.documentElement.scrollLeft}px`;
    
            //座標値のリセット
            menubar.style.position = "";

            const firstChild = target.firstChild;
            target.insertBefore(menubar, firstChild);
            target.style.visibility = "visible"; 
        } else {
            const targetRect = target.getBoundingClientRect();
    
            menubar.style.top = `${targetRect.top - document.documentElement.scrollTop}px`;
            menubar.style.left = `${targetRect.left - document.documentElement.scrollLeft}px`;
    
            menubar.style.position = "absolute";
            target.removeChild(menubar);
            document.body.appendChild(menubar);
            target.style.visibility = "hidden";
        }
    });
    
    // hiddenDiv.addEventListener("click", function () {
        
    //     if (target.style.display === "none") {
    //         const menubarRect = menubar.getBoundingClientRect();
            
    //         target.style.top = `${menubarRect.top - document.documentElement.scrollTop}px`;
    //         target.style.left = `${menubarRect.left - document.documentElement.scrollLeft}px`;
    //         menubar.style.position = "";
    
    //         const firstChild = target.firstChild;
    //         target.insertBefore(menubar, firstChild);
            
    //         target.style.display = "flex";
    //     } else {
    //         const targetRect = target.getBoundingClientRect();
    
    //         menubar.style.top = `${targetRect.top - document.documentElement.scrollTop}px`;
    //         menubar.style.left = `${targetRect.left - document.documentElement.scrollLeft}px`;
    
    //         menubar.style.position = "absolute";
    //         target.removeChild(menubar);
    //         document.body.appendChild(menubar);
    //         target.style.display = "none";            
    //     }
    // });    

    return {menubar, draggableDiv, hiddenDiv};
}

export function MakeScrollContainer(w, h) {

    //ScrollContainerを格納する
    const flexContainerColumn = document.createElement('div');
    flexContainerColumn.className  = 'flexContainerColumn';    
    flexContainerColumn.style.display = "flex";
    flexContainerColumn.style.flexGrow = '1';
    flexContainerColumn.style.flexDirection = "column";   
    
    //height決定要素
    const scrollContainer = document.createElement('div');
    scrollContainer.className  = "ScrollContainer";
    scrollContainer.style.flexGrow = '1';
    scrollContainer.style.border = "1px solid black";
    scrollContainer.style.overflow = "scroll";
    //scrollContainer.style.width = w  + "px";
    //scrollContainer.style.height = h + "px";        
    //scrollContainer.style.width = 300 + "px";
    //scrollContainer.style.height = 300 + "px";   

    flexContainerColumn.appendChild(scrollContainer);
    return flexContainerColumn;
}

export function MakeStatusbar(target) {  

    let min_w = 100;
    let min_h = 100;

    const statusbar = document.createElement('div');
    statusbar.style.border = '1px solid black';
    statusbar.style.display = 'flex';
    statusbar.style.flexDirection = "row";
    statusbar.style.justifyContent = "flex-end"; 

    ////////////////////////////////////////////////////////////////////////////////////////
    //スケール

    const scaleDiv = document.createElement('div');
    scaleDiv.textContent = "スケール";
    scaleDiv.style.border = "1px solid black";
    scaleDiv.style.cursor = 'pointer';
    statusbar.appendChild(scaleDiv);

    let isDragging = false;
    let startY;
    let startX;    

    let startLeft;
    let startTop;
    let startWidth;
    let startHeight;
    
    scaleDiv.addEventListener("mousedown", function (event) {
        isDragging = true;
        startX = event.clientX;
        startY = event.clientY;
    
        let rect = target.getBoundingClientRect();
    
        startLeft = rect.left;
        startTop = rect.top;
        startWidth = rect.width;
        startHeight = rect.height;
    
        if (startWidth < min_w) {
            startWidth = min_w;
        }
        if (startHeight < min_h) {
            startHeight = min_h;
        }
    });
    
    window.addEventListener("mousemove", function (event) {
        if (isDragging) {
            // 横方向の変更
            const deltaX = event.clientX - startX;
            const newWidth = startWidth + deltaX;
            // 縦方向の変更
            const deltaY = event.clientY - startY;
            const newHeight = startHeight + deltaY;
    
            let scaleRect = scaleDiv.getBoundingClientRect();
    
            if (newWidth > min_w && scaleRect.left >= startLeft) {
                target.style.width = newWidth + "px";
            }
            if (newHeight > min_h && scaleRect.top >= startTop) {
                target.style.height = newHeight + "px";
            }
        }
    });

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

export function SetupMainFrame(x, y, w, h) {

    const menubar_h = 30;
    const statusbar_h = 30;

    //Menu, ScrollContainer, Statusをもつ。
    const flexContainerColumn = document.createElement('div');
    flexContainerColumn.id = 'mainFrame';
    flexContainerColumn.style.position = "absolute";
    flexContainerColumn.style.width = w + "px";
    flexContainerColumn.style.height = h + "px";
    flexContainerColumn.style.display = "flex";
    flexContainerColumn.style.flexDirection = "column";
    
    flexContainerColumn.style.backgroundColor = 'rgba(255, 99, 71, 0.3)';

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

    const retMenu = MakeMenubar(flexContainerColumn)
    const menubar = retMenu.menubar;
    const draggableDiv = retMenu.draggableDiv;
    const hiddenDiv = retMenu.hiddenDiv;
    flexContainerColumn.appendChild(menubar);

    ////////////////////////////////////////////////////////////////////////////////////////
   
    const foldingbox = MakeScrollContainer(flexContainerColumn.offsetWidth, h-menubar_h-statusbar_h);
    flexContainerColumn.appendChild(foldingbox);

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

    const retStatus = MakeStatusbar(flexContainerColumn);
    const statusbar = retStatus.statusbar;
    flexContainerColumn.appendChild(statusbar);

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

    return { mainFrame : flexContainerColumn, menubar, foldingbox, statusbar, draggableDiv, hiddenDiv };

}



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