Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I make a function to find the coordinates of an element regardless of its positioning?

I'm making a drag and drop engine in JavaScript, and I don't know how to set the correct position of the dragObj because it changes depending on the parent element's positioning type (Does the dragObj also change depending on its parent's "parent element" ect.?).

So, my dragObj looks like this:

function makeObj(event) {
    var obj = new Object();
    var e = event.target;

    obj.element = e;
    obj.boundElement = null;

    while(e = e.parentNode) {
        if(~e.className.search(/bound/)) { //if(/bound/.test(e.className)) {
            obj.boundElement = e;
            break;
        }
    }

    if(obj.boundElement == null)
        obj.boundElement = document.body;


    // I would like to find the correct minimum bounds with findPos(); however, I need
    // findPos() to work with every type of positioning (absolute, relatice, ect.)

    //var elemPos = findPos(obj.boundElement);
    //obj.minBoundX = elemPos.x;
    //obj.minBoundY = elemPos.y;

    obj.minBoundX = obj.boundElement.offsetLeft + obj.boundElement.offsetWidth - obj.element.offsetWidth;
    obj.minBoundY = obj.boundElement.offsetTop + obj.boundElement.offsetHeight - obj.element.offsetHeight;

    obj.maxBoundX = obj.boundElement.offsetLeft;
    obj.maxBoundY = obj.boundElement.offsetTop;

    setHelperBoxPos(obj);

    obj.posX = event.clientX - obj.element.offsetLeft;
    obj.posY = event.clientY - obj.element.offsetTop;

    return obj;
}

So, when I make a dragObj, I also set its "position" and its bounding element. In a comment portion right before I set the .minBoundX and .minBoundY attributes I explain how I would like to set them; however, it doesn't work because the findPos() function doesn't work.

Here is the findPos() function:

function findPos(obj) { // Donated by `lwburk` on StackOverflow
    var curleft = curtop = 0;
    if (obj.offsetParent) {
        do {
            curleft += obj.offsetLeft;
            curtop += obj.offsetTop;
        } while (obj = obj.offsetParent);
        return { x: curleft, y: curtop };
    }
}

I believe this function works if the bounding element has position: absolute; set, but I want the user to be able to set its positioning type. Also, the bounding element is set by the .bound class, and the dragObj is set by the .drag class.

Here's the HTML:

<div id="min" class="helper-box" style="border: 1px solid blue;"></div>
<div id="max" class="helper-box" style="border: 1px solid red;"></div>

<div id="center">
    <h1>Hello World! <hr /></h1>
    <div id="box" class="bound">
        <p class="drag square"> One </p>
        <p class="drag square"> Two </p>
    </div>
</div>

And here is the CSS:

@charset "utf-8";
/* CSS Document */


* {
    padding: 0px;
    margin: 0px;
}

body {
    background-color:#DFDFDF;
}

.drag {
    position: absolute;
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none;
}

.bound {
    ;
}

.square {
    width: 100px;
    height: 100px;
    background: #1047A9;
    cursor:move;
    border-radius: 25px;
    -moz-border-radius: 25px;
}

#center {
    width: 500px;
    margin: auto;
    margin-top: 50px;
    background-color: #29477F;
    color: #E8E8E8;
    text-align: center;
    border-radius: 25px;
    -moz-border-radius: 25px;
}

#box {
    background-color: #009EBE;
    height: 275px;
    border-radius: 0 0 25px 25px;
    -moz-border-radius: 0 0 25px 25px;
    opacity: 1.0;
}

.helper-box {
    position: absolute;
    width: 5px;
    height: 5px;
}    

And here is the entire engine:

// JavaScript Document

var dragObj;

document.addEventListener("mousedown", down, false);

function down(event) {
    if(~event.target.className.search(/drag/)) {
        dragObj = makeObj(event);
        dragObj.element.style.zIndex="100";
        document.addEventListener("mousemove", freeMovement, false);
    }
}

function freeMovement(event) {

    if (typeof(dragObj.element.mouseup) == "undefined")
        document.addEventListener("mouseup", drop, false);
    //Prevents redundantly adding the same event handler repeatedly

    dragObj.element.style.left = Math.max(dragObj.maxBoundX, Math.min(dragObj.minBoundX, event.clientX - dragObj.posX)) + "px";
    dragObj.element.style.top = Math.max(dragObj.maxBoundY, Math.min(dragObj.minBoundY, event.clientY - dragObj.posY)) + "px";
}

function drop() {
    dragObj.element.style.zIndex="1";

    document.removeEventListener("mousemove", freeMovement, false);
    document.removeEventListener("mouseup", drop, false);
    //alert("DEBUG_DROP");
}


function makeObj(event) {
    var obj = new Object();
    var e = event.target;

    obj.element = e;
    obj.boundElement = null;

    while(e = e.parentNode) {
        if(~e.className.search(/bound/)) { //if(/bound/.test(e.className)) {
            obj.boundElement = e;
            break;
        }
    }

    if(obj.boundElement == null)
        obj.boundElement = document.body;


    // I would like to find the correct minimum bounds with findPos(); however, I need
    // findPos() to work with every type of positioning (absolute, relatice, ect.)

    //var elemPos = findPos(obj.boundElement);
    //obj.minBoundX = elemPos.x;
    //obj.minBoundY = elemPos.y;

    obj.minBoundX = obj.boundElement.offsetLeft + obj.boundElement.offsetWidth - obj.element.offsetWidth;
    obj.minBoundY = obj.boundElement.offsetTop + obj.boundElement.offsetHeight - obj.element.offsetHeight;

    obj.maxBoundX = obj.boundElement.offsetLeft;
    obj.maxBoundY = obj.boundElement.offsetTop;

    setHelperBoxPos(obj);

    obj.posX = event.clientX - obj.element.offsetLeft;
    obj.posY = event.clientY - obj.element.offsetTop;

    return obj;
}

function findPos(obj) { // Donated by `lwburk` on StackOverflow
    var curleft = curtop = 0;
    if (obj.offsetParent) {
        do {
            curleft += obj.offsetLeft;
            curtop += obj.offsetTop;
        } while (obj = obj.offsetParent);
        return { x: curleft, y: curtop };
    }
}

    function setHelperBoxPos(obj) {
        var minBox = document.getElementById('min');
        minBox.style.left = obj.minBoundX + 'px';
        minBox.style.top = obj.minBoundY + 'px';

        var maxBox = document.getElementById('max');
        maxBox.style.left = obj.maxBoundX + 'px';
        maxBox.style.top = obj.maxBoundY + 'px';
    }

I have also made a jsfiddle for your convenience: http://jsfiddle.net/2XGhK/

So, how do I make a findPos() function which allows for different kinds of positioning. Will I need to make another findPos() function to allow the dragObj to be any kind of positioning as well?

Important Please do not recommend using a library (unless you suggest looking at it for tips on how they deal with positioning).The reason is that I am just learning the language and building things helps me do just that. What's the point in learning a library before I even understand the language?

Most importantly, I greatly appreciate all of your help. Thank you.

like image 812
Dbz Avatar asked Mar 09 '11 03:03

Dbz


3 Answers

In developing Javascript, jQuery really is your best friend, as it simplifies many of these (potentially annoying) tasks. I totally dig the desire to learn without them though; it's a great way to become proficient.

Take a look at jQuery's docs and implementation for position and offset, and hopefully that will give you a good idea for a starting place.

Docs:

http://api.jquery.com/position/

http://api.jquery.com/offset/

Source:

https://github.com/jquery/jquery/blob/master/src/offset.js

like image 120
Dominic Avatar answered Oct 22 '22 07:10

Dominic


I don't know if this is exactly what you need but implementing drag&drop with javascript requires a lot of efforts and testing, especially when you are dealing with parent-children dragging.

Try this one:

http://interface.eyecon.ro/demos/drag.html

Maybe you can read its source code and use it to fit your needs.

like image 2
ngduc Avatar answered Oct 22 '22 08:10

ngduc


Simply make drag object's position absolute -

obj.element = e;
obj.element.style.position = "absolute";
obj.boundElement = null;

btw, thanks for using jsfiddle, it helps.

like image 1
EGL 2-101 Avatar answered Oct 22 '22 08:10

EGL 2-101