Как отследить координаты перетаскиваемого объекта относительно другого

При нажатии на "+" создается новый элемент. Его можно перемещать между блоками. Если его отпустить на каком-то элементе, он вставится после него. Нужно сделать так, чтобы при дропе на какой-то элемент, если перетаскиваемый элемент смещен правее, относительно элемента на который он дропается, он дропался справа, а если смещен левее, дропался левее. Как это реализовать? У меня есть вариант взять координаты центра объекта на который дропаем как X = 0, Y = 0, и если позиция дропаемого элемента относительно Y будет > 0 то дропать справа, если < 0 , то дропать слева. Но как реализовать на языке JS я пока не понимаю. Можно ли так реализовать решение или есть другие варианты?

 let counter = 0;
    let currentDrag;

    document.addEventListener('click', e => {
        if (e.target.classList.contains('new-item')) {
            const newItem = createItem();
            const zone = getCurrentZone(e.target);

            zone.insertBefore(newItem, zone.lastElementChild)
        }
    });

    document.addEventListener('dragstart', (e) => {
        const zone = getCurrentZone(e.target);

        if (zone) {
            currentDrag = { startZone: zone, node: e.target };
            e.dataTransfer.setData('text/html', 'dragstart');
        }
    });

    document.addEventListener('dragover', (e) => {
        const zone = getCurrentZone(e.target);

        if (zone) {
            e.preventDefault();
        }
    });

    document.addEventListener('drop', (e) => {
        e.preventDefault();
        const zone = getCurrentZone(e.target);

        if (currentDrag) {
            if (zone && currentDrag.startZone !== zone) {
                if (e.target.classList.contains('item')) {
                    zone.insertBefore(currentDrag.node, e.target.nextElementSibling);
                } else {
                    zone.insertBefore(currentDrag.node, zone.lastElementChild);
                }
            }
            if (zone && currentDrag.startZone === zone) {
                if (e.target.classList.contains('item')) {
                    zone.insertBefore(currentDrag.node, e.target.nextElementSibling);
                } else {
                    zone.insertBefore(currentDrag.node, zone.lastElementChild);
                }
            }

            currentDrag = null;
        } else {
            const dt = e.dataTransfer;

            if (dt.files && dt.files.length) {
                for (const file of dt.files) {
                    const reader = new FileReader();

                    reader.readAsDataURL(file);
                    reader.addEventListener('load', () => {
                        const item = createItem(reader.result);
                        zone.insertBefore(item, zone.lastElementChild);
                    });
                }
            }
        }
    });

    function createItem(background) {
        const newDiv = document.createElement('div');

        counter++;

        if (background) {
            newDiv.style.background = `url(${background})`;
            newDiv.style.backgroundSize = `cover`;
        } else {
            newDiv.textContent = counter;
        }
        newDiv.classList.add('item');
        newDiv.draggable = true;

        return newDiv;
    }

    function getCurrentZone(from) {
        // return from.parentNode;
        // return from.closest('.drop-zone');

        do {
            if (from.classList.contains('drop-zone')) {
                return from;
            }
        } while (from = from.parentElement);

        return null;
    }
 .container {
            display: flex;
            padding: 30px;
            height: 500px;
        }

        .drop-zone {
            display: flex;
            flex-basis: 50%;
            flex-wrap: wrap;
            align-content: start;
            padding: 15px;
            border: 3px dashed #b3b3b3;
            border-radius: 10px;
        }

        .item, .new-item {
            margin: 0 10px 10px 0;
            display: flex;
            justify-content: center;
            align-items: center;
            color: hsla(0, 0%, 40%, 1);
        }

        .item, .new-item {
            border: 2px dashed #783db3;
            border-radius: 10px;
            width: 50px;
            height: 50px;
            cursor: move;
            user-select: none;
        }

        .new-item {
            cursor: pointer;
            border: 2px dashed #b3b3b3;
            font-size: 16px;
        }
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="container text-center">
<div class="container">
    <div class="drop-zone">
        <div class="new-item">+</div>
    </div>

    <div class="drop-zone">
        <div class="new-item">+</div>
    </div>

    <div class="drop-zone">
        <div class="new-item">+</div>
    </div>

    <div class="drop-zone">
        <div class="new-item">+</div>
    </div>
</div>


Ответы (1 шт):

Автор решения: Max M

В общем задачу я решил так, но можно ли как-то отслеживать если я дропаю элемент на пустое место, а не на какой-то элемент? Если я дропаю в пустое место, но ближе к какому-то из соседних элементов, он дропался после него

let counter = 0;
    let currentDrag;

    document.addEventListener('click', e => {
        if (e.target.classList.contains('new-item')) {
            const newItem = createItem();
            const zone = getCurrentZone(e.target);

            zone.insertBefore(newItem, zone.lastElementChild)
        }

    });

    document.addEventListener('dragstart', (e) => {
        const zone = getCurrentZone(e.target);

        if (zone) {
            currentDrag = { startZone: zone, node: e.target };
            e.dataTransfer.setData('text/html', 'dragstart');
        }
    });

    document.addEventListener('dragover', (e) => {
        const zone = getCurrentZone(e.target);

        if (zone) {
            e.preventDefault();
        }
    });

    document.addEventListener('drop', (e) => {
        e.preventDefault();


        const zone = getCurrentZone(e.target);


        if (currentDrag) {
         /* if (e.target.classList.contains('item')) {
                let centerX = e.target.offsetLeft + e.target.offsetWidth / 2;

                ondragend = function (event) {
                    if (event.clientX < centerX) {
                        console.log('leftside', e.target.textContent);


                    }
                     else if (event.clientX > centerX) {
                        console.log('rightside', e.target.textContent);

                    }
                }

            }*/



            if (zone && currentDrag.startZone !== zone || currentDrag.startZone === zone) {
                if (e.target.classList.contains('item')) {
                    let centerX = e.target.offsetLeft + e.target.offsetWidth / 2;

                    ondragend = function (event) {
                        if (event.clientX < centerX) {
                            console.log('leftside', e.target.textContent);
                            zone.insertBefore(event.target, e.target);

                        }
                        else if (event.clientX > centerX) {
                            console.log('rightside', e.target.textContent);
                            zone.insertBefore(event.target, e.target.nextElementSibling);


                        }
                    }
                   // zone.insertBefore(currentDrag.node, e.target.nextElementSibling);

                } else {
                   zone.insertBefore(currentDrag.node, zone.lastElementChild);
                }
            }


            currentDrag = null;
        } else {
            const dt = e.dataTransfer;

            if (dt.files && dt.files.length) {
                for (const file of dt.files) {
                    const reader = new FileReader();

                    reader.readAsDataURL(file);
                    reader.addEventListener('load', () => {
                        const item = createItem(reader.result);
                        zone.insertBefore(item, zone.lastElementChild);
                    });
                }
            }
        }
    });

    function createItem(background) {
        const newDiv = document.createElement('div');

        counter++;

        if (background) {
            newDiv.style.background = `url(${background})`;
            newDiv.style.backgroundSize = `cover`;
        } else {
            newDiv.textContent = counter;
        }
        newDiv.classList.add('item');
        newDiv.draggable = true;

        return newDiv;
    }

    function getCurrentZone(from) {
        // return from.parentNode;
        // return from.closest('.drop-zone');

        do {
            if (from.classList.contains('drop-zone')) {
                return from;
            }
        } while (from = from.parentElement);

        return null;
    }
.container {
            display: flex;
            padding: 30px;
            height: 500px;
        }

        .drop-zone {
            display: flex;
            flex-basis: 50%;
            flex-wrap: wrap;
            align-content: start;
            padding: 15px;
            border: 3px dashed #b3b3b3;
            border-radius: 10px;
        }

        .item, .new-item {
            margin: 0 10px 10px 0;
            display: flex;
            justify-content: center;
            align-items: center;
            color: hsla(0, 0%, 40%, 1);
        }

        .item, .new-item {
            border: 2px dashed #783db3;
            border-radius: 10px;
            width: 50px;
            height: 50px;
            cursor: move;
            user-select: none;
        }

        .new-item {
            cursor: pointer;
            border: 2px dashed #b3b3b3;
            font-size: 16px;
        }
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
    
</head>
<body class="container text-center">
<div class="container">
    <div class="drop-zone">
        <div class="new-item">+</div>
    </div>

    <div class="drop-zone">
        <div class="new-item">+</div>
    </div>


</div>
</body>
</html>

→ Ссылка