开发者

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.boundElem开发者_StackOverflowent.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.


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


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.


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.


How useful is this code fragment?

var position = {};
var dragObj = "";
function initDrag(e){
    e = e || window.event;

    position.objLeft = parseInt(dragObj.style.left);
    position.objTop = parseInt(dragObj.style.top);
    if( isNaN(position.objLeft) )
        position.objLeft = 0;
    if( isNaN(position.objTop) )
        position.objTop = 0;        
    position.startX = e.clientX;
    position.startY = e.clientY;
    if( document.addEventListener )
    {
        e.preventDefault();
        dragObj.addEventListener('mousemove',startDrag,false);
        dragObj.addEventListener('mouseup',stopDrag,false);
        dragObj.addEventListener('mouseout',stopDrag,false);
    }
    else
    {
        dragObj.attachEvent('onmousemove',startDrag);
        dragObj.attachEvent('onmouseup',stopDrag);
        dragObj.attachEvent('onmouseout',stopDrag);
    }
}

function startDrag(e){
    e = e || window.event;

    dragObj.style.left = position.objLeft+e.clientX-position.startX;
    dragObj.style.top = position.objTop+e.clientY-position.startY;
    return false;
}


Actually when working with finding the elements position, you use its offsetLeft(/Top). And each element have an offsetParent from where that position origins. When you drag an element, it is good to know it's position according to the whole document (read ), so you get a more precise point. So to find an elementes position you must create the following function:

function findPos(elm) {
    var testElm = elm, pos = {x:0, y:0};
    while( !!testElm && testElm.tagName.toLowerCase() !== "body" ) {
        pos.x += testElm.offsetLeft;
        pos.y += testElm.offsetTop;
        // important to use offsetParent instead of just parentNode,
        // as offsetParent will be where the element gets its offset from.
        // And that is not necesarily it parentNode!
        testElm = testElm.offsetParent;
    }
    return pos;
}

But i agree with some of the others, that jQuery DO simplify alot of these tedious calculations. But I really think it is best to get to know JS before you use the API's as it give you an understading of what JS really is.

PS. Dont call this method for every mousemove event, as it will compromise performance. Intead save the value on the element (<your node>["startoffset"] = findPos(<your node>)), and then use it in the mousemove script to find its new position. Actually it is good practice, to save as many values you can upon mousedown, as mousemove will be called ALOT, and therefore should perform as little calculations/traversing as possible.

See it in action: http://jsfiddle.net/fnwxu/22/


Simplest solution:

Set the element to visibility:hidden. Clone the element, set the clone to position:absolute, perform dragging. Once dragging stops (i.e. the user has released the left mouse button), replace the clone with the original element.

This allows you to keep the element in flow for the duration of the draggable operation. That way if the element cannot be dropped in location y (for example), you can have the helper snap back to the original location.


I am a beginner in JavaScript. I found your question very interesting and thought of Googling it. I found these two links which may be of use to you:

http://www.codingforums.com/archive/index.php/t-84055.html
http://www.codeproject.com/KB/scripting/DragDrop_Part-2.aspx

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜