Change position of divs with jquery drag an drop

59,218

Solution 1

Here's a working example: http://jsfiddle.net/Gajotres/zeXuM/

I think all of your problems are solved here.

  • enable/disable dropping works
  • elements returning no longer drops them below other elements
  • elements returning no longer hides/remove them
  • better elements positioning (it looks better)
  • it works on mobile devices (tested on Android 4.1.1 Chrome and iPhone)

Here's a jQuery code used:

$(document).on('pageshow', '#index', function(){       
    $(".dragzones").draggable({
        start: handleDragStart,
        cursor: 'move',
        revert: "invalid",
    });
    $(".dropzones").droppable({
        drop: handleDropEvent,
        tolerance: "touch",              
    });
});

function handleDragStart (event, ui) { }

function handleDropEvent (event, ui) {
    if (ui.draggable.element !== undefined) {
        ui.draggable.element.droppable('enable');
    }
    $(this).droppable('disable');
    ui.draggable.position({of: $(this),my: 'left top',at: 'left top'});
    ui.draggable.draggable('option', 'revert', "invalid");
    ui.draggable.element = $(this);
}

    // This is a fix for mobile devices

/iPad|iPhone|Android/.test( navigator.userAgent ) && (function( $ ) {

var proto =  $.ui.mouse.prototype,
_mouseInit = proto._mouseInit;

$.extend( proto, {
    _mouseInit: function() {
        this.element
        .bind( "touchstart." + this.widgetName, $.proxy( this, "_touchStart" ) );
        _mouseInit.apply( this, arguments );
    },

    _touchStart: function( event ) {
         this.element
        .bind( "touchmove." + this.widgetName, $.proxy( this, "_touchMove" ) )
        .bind( "touchend." + this.widgetName, $.proxy( this, "_touchEnd" ) );

        this._modifyEvent( event );

        $( document ).trigger($.Event("mouseup")); //reset mouseHandled flag in ui.mouse
        this._mouseDown( event );

        //return false;           
    },

    _touchMove: function( event ) {
        this._modifyEvent( event );
        this._mouseMove( event );   
    },

    _touchEnd: function( event ) {
        this.element
        .unbind( "touchmove." + this.widgetName )
        .unbind( "touchend." + this.widgetName );
        this._mouseUp( event ); 
    },

    _modifyEvent: function( event ) {
        event.which = 1;
        var target = event.originalEvent.targetTouches[0];
        event.pageX = target.clientX;
        event.pageY = target.clientY;
    }

});

})( jQuery );

Original author of a touchFix plugin used in this example is Oleg Slobodskoi.

Solution 2

EDITED:

On each successful drop you can save the last drop object on the draggable. So when the draggable moves to another drop object you can enable the droppable of the previous drop object.

See below:

Try to modify the handleDropEvent to:

function handleDropEvent(event, ui) {
    if (ui.draggable.lastDropObject !== undefined) {
        ui.draggable.lastDropObject.droppable('enable');
    }
    var dropObject = $(this);
    dropObject.droppable('disable');
    ui.draggable.position({
        of: $(this),
        my: 'left top',
        at: 'left top'
    });
    ui.draggable.draggable('option', 'revert', "invalid");
    ui.draggable.lastDropObject = dropObject;
}

Full working example based on your source code:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

    <head>
        <title>Drag & Drop with jQuery</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <meta name="title" content="" />
        <meta name="keywords" content="" />
        <meta name="description" content="" />
        <link href="http://www.didaktik.physik.uni-muenchen.de/forschung/elektronenbahnen/Betaversion/Tests/Styletest.css" rel="stylesheet" type="text/css" />
        <script type="text/javascript" src="http://www.didaktik.physik.uni-muenchen.de/forschung/elektronenbahnen/Betaversion/Tests/../Alle Seiten/JS/jquery.js"></script>
        <script type="text/javascript" src="http://www.didaktik.physik.uni-muenchen.de/forschung/elektronenbahnen/Betaversion/Tests/../Alle Seiten/JS/jquery-ui-1.8.23.custom.min.js"></script>
        <script type="text/javascript" src="http://www.didaktik.physik.uni-muenchen.de/forschung/elektronenbahnen/Betaversion/Tests/../Alle Seiten/JS/jquery.ui.touch-punch.min.js"></script>
        <script type="text/javascript">
            var test;
            $(init);

            function init() {
                $(".dragzones")
                    .draggable({
                    start: handleDragStart,
                    cursor: 'move',
                    revert: "invalid"
                });
                $(".dropzones")
                    .droppable({
                    drop: handleDropEvent,
                    tolerance: "touch",
                });
            };

            function handleDragStart(event, ui) {}

            function handleDropEvent(event, ui) {
                if (ui.draggable.lastDropObject !== undefined) {
                    ui.draggable.lastDropObject.droppable('enable');
                }
                var dropObject = $(this);
                dropObject.droppable('disable');
                ui.draggable.position({
                    of: $(this),
                    my: 'left top',
                    at: 'left top'
                });
                ui.draggable.draggable('option', 'revert', "invalid");
                ui.draggable.lastDropObject = dropObject;
            }



            function check() {
                var i = 1;
                var richtige = 0;
                while (i <= 10) {
                    var j = i + 10;
                    if (document.getElementById('zone' + j)
                        .innerHTML.search('drag' + i + '"') == 27) {
                        document.getElementById('zone' + j)
                            .style.border = '2px solid green';
                        richtige++;
                    } else {
                        document.getElementById('zone' + j)
                            .style.border = '2px solid red';
                    }
                    i++;
                    alert(document.getElementById(zone + j)
                        .innerHTML);
                };
            }
        </script>
    </head>

    <body>
        <div id="alles">
            <div id="hintergrundbildZuordnungsaufgaben">
                <img src="http://www.didaktik.physik.uni-muenchen.de/forschung/elektronenbahnen/Betaversion/Tests/../E-Feld/Bilder/Versuchsaufbau-Elektronenablenkroehre-unbeschriftet.png" width="740">
            </div>
            <div class="dropzones" id="zone1">
                <div class="dragzones" id="drag1">Item 1</div>
            </div>
            <div class="dropzones" id="zone2">
                <div class="dragzones" id="drag2">Item 2</div>
            </div>
            <div class="dropzones" id="zone3">
                <div class="dragzones" id="drag3">Item 3</div>
            </div>
            <div class="dropzones" id="zone4">
                <div class="dragzones" id="drag4">Item 4</div>
            </div>
            <div class="dropzones" id="zone5">
                <div class="dragzones" id="drag5">Item 5</div>
            </div>
            <div class="dropzones" id="zone6">
                <div class="dragzones" id="drag6">Item 6</div>
            </div>
            <div class="dropzones" id="zone7">
                <div class="dragzones" id="drag7">Item 7</div>
            </div>
            <div class="dropzones" id="zone8">
                <div class="dragzones" id="drag8">Item 8</div>
            </div>
            <div class="dropzones" id="zone9">
                <div class="dragzones" id="drag9">Item 9</div>
            </div>
            <div class="dropzones" id="zone10">
                <div class="dragzones" id="drag10">Item 10</div>
            </div>
            <div class="dropzones" id="zone11"></div>
            <div class="dropzones" id="zone12"></div>
            <div class="dropzones" id="zone13"></div>
            <div class="dropzones" id="zone14"></div>
            <div class="dropzones" id="zone15"></div>
            <div class="dropzones" id="zone16"></div>
            <div class="dropzones" id="zone17"></div>
            <div class="dropzones" id="zone18"></div>
            <div class="dropzones" id="zone19"></div>
            <div class="dropzones" id="zone20"></div>
            <button id="check" value="Check" onclick="check()">Check</button>
        </div>
    </body>

</html>

I hope this helps.

Solution 3

This is as far as I've gotten:

$ (init);
function init() {
    $(".dragzones").draggable({
        start: handleDragStart,
        cursor: 'move',
        revert: 'invalid',
        opacity: .5,
    });
    $(".dropzones").droppable({
        drop: handleDropEvent,
        tolerance: "touch",
        out: handleDropRemove
    });

    //prevents dragging to filled default droppables on start
    $(".dropzones").each(function(){
    if ($(this).html().length) { 
        $(this).addClass('taken'); 
    }
    });
}



function handleDragStart (event, ui) {} 
function handleDropRemove(event, ui) {
        //allows drop after removal
       $(this).removeClass('taken');
}      
function handleDropEvent (event, ui) {
   if ($(this).hasClass('taken')) {
       //rejects drop if full
        ui.draggable.draggable('option', 'revert', true);
    } else {
        //accepts drop if enpty
        ui.draggable.position({of: $(this), my: 'left top', at: 'left top'});
        $(this).addClass('taken');
   }
}

Working Demo Here

There are a couple issues I'm trying to sort still. Seems if the cursor hits the very bottom of a drop zone when it is filled, the out function is being called. I can't seem to figure out how to prevent that.

Solution 4

You "Example" link looks like working correctly. But I would like to give generic solution to your problem.

Basically you watch status of dropzone. When user drops the item, you check if dropzone is available or not. if not set "revert" true.

function handleDropEvent (event, ui) {
    if ($(this).hasClass('occupied')) {
        ui.draggable.draggable('option', 'revert', true);
        return false;
    }
    $(this).append(ui.draggable);
    ui.draggable.position({of: $(this), my: 'left top', at: 'left top'});
    ui.draggable.css('z-index', 0);
    setTimeout(validateDropzones, 0);
}

Full working example is here: http://jsfiddle.net/jaygiri/SRPm2/

Thanks.

Share:
59,218

Related videos on Youtube

Stefan
Author by

Stefan

Updated on July 09, 2022

Comments

  • Stefan
    Stefan almost 2 years

    I'm trying to build a website where the user can drag some items (one item in on div) to other divs on the page. It' not a table or so, just divs somewhere on the page.

    With html5 drag&drop it works well, now I try to do this for mobile devices. I can drag items to divs, drop them there and block this dropzone, because only one element should be in a dropzone. I also can drag this element to another div or somewhere else on the page (droppable areas only work on first time a div is dropped) if I've had make a mistake but then I cannot drop another item in the div which is now empty again.

    How can I enable dropping in this Dropzone again?

    And is it possible to two change the position of two divs if one is dragged on another?

    Here is the relevant part of my code:

    <script type="text/javascript">
    $ (init);
    function init() {
        $(".dragzones").draggable({
            start: handleDragStart,
            cursor: 'move',
            revert: "invalid",
        });
        $(".dropzones").droppable({
            drop: handleDropEvent,
            tolerance: "touch",              
        });
    }
    function handleDragStart (event, ui) {}       
    function handleDropEvent (event, ui) {
        $(this).droppable('disable');
        ui.draggable.position({of: $(this), my: 'left top', at: 'left top'});
        ui.draggable.draggable('option', 'revert', "invalid");
    }
    </script>
    <body>
    <div id="alles">
    <div class="dropzones" id="zone1"><div class="dragzones" id="drag1">Item 1</div></div>
    <div class="dropzones" id="zone2"><div class="dragzones" id="drag2">Item 2</div></div>
    <div class="dropzones" id="zone3"><div class="dragzones" id="drag3">Item 3</div></div>
    <div class="dropzones" id="zone4"><div class="dragzones" id="drag4">Item 4</div></div>
        <div class="dropzones" id="zone11"></div>
        <div class="dropzones" id="zone12"></div>
        <div class="dropzones" id="zone13"></div>
        <div class="dropzones" id="zone14"></div>   
    </div>
    </body>
    

    EDIT: Here is the now working page: Drag&Drop Task

    • nycynik
      nycynik about 11 years
      there are lots of examples using non-html5 drag and drop, thats what I would recommend.
    • Admin
      Admin about 11 years
      I tried the tinyurl example in Chrome and I could very well drop items into emptied dropzones again? What am I missing?
    • j0k
      j0k about 11 years
      @ink.robot nop, it doesn't work. Drop an item on an empty case. Then drag it to the original place and try to drag an other item on the empty case. You can't.
    • EGL 2-101
      EGL 2-101 about 11 years
      UI in Example link of first para works fine. But tiny url example UI shows the behavior described.
  • EGL 2-101
    EGL 2-101 about 11 years
    Sure, it won't. The primary questions looks like: "How can I enable dropping in this Dropzone again?" I just showed a logic to answer it - where we look at status of dropzone. For full touch support to your jq mobile application you need to do this: stackoverflow.com/a/8826958/647736
  • Kamlesh
    Kamlesh over 4 years
    In my case Droppable area is not fixed. User can move and put any element on any position. any suggestion? Please share.