JavaScript drag and drop plus content shift

More...

Demo is based on REDIPS.drag JavaScript library with enabled shift drop mode. After element is dropped to the cell, other elements will be shifted to make room. Animation is optional and can be turned off. The presented demo (with three tables - normal, colspan / rowspan and marked cells) can be a good start point for various sorting Web applications.

A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
C
Trash
Animation
Show confirm delete dialog

Shift after options:
Default - shift content only if DIV element is dropped to the non empty cell
Delete - same as "default" + shift content after element is deleted
Always - always shift table content

Shift options:
horizontal 1 - element shift can affect more rows
horizontal 2 - each row is treated separately
vertical 1 - element shift can affect more columns
vertical 2 - each column is treated separately

Overflow:
bunch - overflowed DIV will stay in last cell (default)
delete - overflow will be deleted
source - overflow will me moved to the source TD
HTMLElement - overflow will me moved to user defined TD

Boolean public property animation.shift defines whether shift animation will be turned off or on. Default is off and table content will be shifted instantly. If animation is turned on, then dragging will not be possible while animation lasts. This was needed to prevent possible collisions between animation and dragging.

Drag and drop DIV element inside the same table will actually sort table content. Table content will be shifted to left or to the right side. If new element is dropped to the table, then table content will be shifted to the right. Repeating will result with accumulation of DIV elements in the bottom last table cell. Here is JavaScript code needed for shift table content:

var redipsInit,
    setTable,
    shiftMode,
    overflow,
    shiftAnimation,
    shiftAfter,
    toggleConfirm,
    counter = 0;

// redips initialization
redipsInit = function () {
    // reference to the REDIPS.drag library
    var rd = REDIPS.drag;
    // initialization
    rd.init();
    // set mode option to "shift"
    rd.dropMode = 'shift';
    // enable animation on shifted elements
    rd.animation.shift = true;
    // set animation loop pause
    rd.animation.pause = 20;
    // set TD for overflow elements (initially)
    rd.shift.overflow = document.getElementById('overflow');
    // add counter to cloned element name
    // (after cloned DIV element is dropped to the table)
    rd.event.clonedDropped = function () {
        // increase counter
        counter++;
        // append to the DIV element name
        rd.obj.innerHTML += counter;
    };
};

// set current table
setTable = function (e) {
    var value = e.options[e.selectedIndex].value,
        tables = document.getElementById('drag').getElementsByTagName('table'),
        i;
    // loop goes through all fetched tables within drag container
    for (i = 0; i < tables.length; i++) {
        // skip mini table
        if (tables[i].id === 'mini') {
            continue;
        }
        // show selected table
        else if (tables[i].id === value) {
            tables[i].style.display = '';
        }
        // hide all other tables
        else {
            tables[i].style.display = 'none';
        }
    }
};

// set shift mode
shiftMode = function (radio) {
    REDIPS.drag.shift.mode = radio.value;
};

// set overflow
overflow = function (radio) {
    if (radio.value === 'user') {
        REDIPS.drag.shift.overflow = document.getElementById('overflow');
    }
    else {
        REDIPS.drag.shift.overflow = radio.value;
    }
};

// enable / disable animation
shiftAnimation = function (chk) {
    REDIPS.drag.animation.shift = chk.checked;
};

// enable / disable shift after element is deleted
shiftAfter = function (chk) {
    REDIPS.drag.shift.after = chk.value;
};

// toggles trash_ask parameter defined at the top
toggleConfirm = function (chk) {
    if (chk.checked === true) {
        REDIPS.drag.trash.question = 'Are you sure you want to delete DIV element?';
    }
    else {
        REDIPS.drag.trash.question = null;
    }
};

// add onload event listener
if (window.addEventListener) {
    window.addEventListener('load', redipsInit, false);
}
else if (window.attachEvent) {
    window.attachEvent('onload', redipsInit);
}

Label of cloned elements is increased on every element cloning. Please see how event.clonedDropped event handler was used. REDIPS.drag has many event handlers hooked in different stages to simplify needed customization.

This entry was posted on October 18, 2011 and is filed under Drag and Drop, JavaScript

Related posts

35 Responses to JavaScript drag and drop plus content shift

  1. dbunic says:

    @Ramprasad - Checkbox definition is simple:

    <input type="checkbox" name="box1" value="R"/>
    <input type="checkbox" name="box2" value="W"/>
    

    Checkboxes should be closed within HTML form. After user clicks on form submit, browser will send values box1=R&box2=W if both checkboxes are checked. In case of unchecked checkbox, browser will not send anything. So, on the server side you should accept form parameters box1 and box2. Here is example in PHP:

    $box1 = $_REQUEST['box1'];
    $box1 = $_REQUEST['box2'];
    

    Now parameters are accepted and can be stored to the database. Hope this tips will be helpful.

  2. Yohan says:

    Hi,

    First, please excuse me if I make writing mistakes. I'm French and my English level is not very good.

    Next, your library is pretty cool, I have been using it for 2 days and I discoverd a lot of things.

    BUT, I have a little question : How can I do to sort my photos, using your "shift" option without leaving empty cells.

    For example, in this page, if you move the "J" down, you will have an empty cell between "I" and "K".

    How can I do this ?

    Thanks a lot for your help.

    Yohan.

  3. Waqas says:

    Everything was working fine then I tried to call Perform action using ICallbackEventHandler interface that really called it but color was not changing, after that I removed that interface and again switched to __doPostBack but then I'm facing an error that my DIVs become disabled when I drag a single DIV and again enabled on page refresh ...

    Can you help me please?

  4. dbunic says:

    @Yohan - REDIPS.drag is upgraded to version 4.7.4. If shift_after public property is set to always then content will be shifted in your case as well.

    REDIPS.drag.shift_after = 'always';
    
    // if REDIPS.drag is defined as rd
    
    rd.shift_after = 'always';
    

    New feature is shown in this example also. Just check "always" radio button and try to move "J" element to empty table cell.

    @Waqas - Described problems are related to messed events handlers attached to the DIV elements. During REDIPS.drag initialiazation, lib will search for all DIV elements inside drag container and attach onClick event handler (this is a bit simplified description). In the moment when user clicks on DIV element, onMove event handler will be activated on BODY. This way dragged DIV element can be moved accross whole page. When mouse button is released, DIV element will be replaced from source to the current table cell and onMove event handler will be removed. In short, this is described process of how REDIPS.drag works. If you are familiar with Web inspector you can test if event listener exists and try to discover the source of collision. Hope this lines will help you in debugging ...

  5. Stephen says:

    Am loving the script, am using example 13 as base of what we are doing. Am having difficulty in finding out how to align objects so that they align horizontally instead of vertically, so that they wrap onto next line when table cell fills up.

    Could someone explain where I can find the settings for this?

    Thanks

  6. dbunic says:

    @Stephen - Just try to set float:left style. Here is example of DIV CSS styles to place dropped elements horizontally (side by side):

    .drag {
        text-align: center;
        width: 80px;
        height: 30px;
        float: left;
    }
    

    You will need to have wide enough table cells for horizontal alignment. Hope this will solve your problem ...

  7. Stephen says:

    Hi there, thanks for reply, I will try that later on. Stephen

  8. Thomas says:

    First of all, this library is awesome and you are the man.

    Quick Question though. I have been working on one thing for a few days that I can't figure out.

    If I have Horizontal2 turned on, how can I make it so that all DIVs shifts Left so that no spaces are left? (Similar to Yohans example except I want DIVs to stay in their designated rows)

    I know that may be a problem if there are multiple DIVs in the last row, but my table has enough columns where that shouldn't be an issue...Or maybe the top/bottom DIV moves left.

    Again, all DIVs shift left until there are no open TDs in each row.

    Is there a relatively simple way to do this?

  9. dbunic says:

    @Thomas - horizontal2 mode treats rows separately. If content should be shifted in whole table then try with horizontal1 mode. The similar modes are vertical1 and vertical2 (content is shifted verticaly).

    Next, DIV elements are shifted cell by cell. But you can create custom shifting with shift_cells(td1, td2) method. td1 and td2 are source and target table cells.

  10. Thomas says:

    Thanks Darko,

    Sorry for wording the problem poorly. I just meant if horizontal2 is turned on, and I was to take DIv B and place it in say (row 4, column 4) , C,D,E,F in row 0 would shift to the left to fill in the gap, and B would shift in row 4 to the left until it came to another DIV, or R. No Gaps would ever be left in rows.

    I got it working using shift_cells and a loop that starts at the final column of each row, and works its way to the left shifting any cells that have an empty cell to "the left." If there is a shift, it jumps back to the right most column and starts again.

    The only problem I am having is that the animation is awkward from the looping, so I am turning it off during the loop. Sometimes the contents of the cell can shift multiple spaces to the left. Maybe I can fix it with move_object and/or relocate.

    Also what event can I use that will start my loop AFTER all of your standard animated shifts happen?

    Thanks again

  11. dbunic says:

    @Thomas - Thanks for detailed info. Lib is updated with new event handler. So, in REDIPS.drag version 4.7.6, you have myhandler_relocated() event handler. This event handler is triggered after animation is finished in shift mode (actually, myhandler_relocated() event handler is called from relocate() method). If animation is turned off, then new event handler will not be called.

    Hope this will help. If you will still need assistance, I will gladly help.

  12. Thomas says:

    Thanks again for your help Darko.
    The problem was I thought that shift_cells(td1,td2) only shifted adjacent cells, rather then the range of cells. Looping through adjacent cells caused the animation to get a little whacky.

    For anyone else interested, here is a simple starting point so that "gaps" in your rows close themselves, and everything shifts left in its row while horizontal2 is on.

    leftShift = function (rd) {
        var lastCol = 5;
        var mainTable = document.getElementById("mainTable");
        rd.enable_table(false, mainTable);
    
        var pos = rd.get_position();
        var startRow = pos[4];
        var startCol = pos[5];
        var finishRow = pos[1];
        var finishCol = pos[2];
        var leftTd;
        var rightTd;
        var trList;
        var tdList;
    
        if (startRow != finishRow) {
            trList = mainTable.getElementsByTagName('tr');
            tdList = trList[startRow].getElementsByTagName('td');
    
            rightTd = tdList[lastCol];
            leftTd = tdList[startCol];
    
            rd.shift_cells(leftTd, rightTd);
        }
        rd.enable_table(true, mainTable);
    };
    
  13. dbunic says:

    @Thomas - Thank you very much for posting a solution. Your JS code will surely be helpful for others. Cheers!

  14. emerson says:

    Hello Mr.dbunic,
    Thank you for your great ideas.
    How may we save the position of the above Table2 example?

    I would like to save the position to database.
    Can it be done? with your example above?
    If too much work, you may charge if you need.

    Once more, Thank you sir.

  15. dbunic says:

    @emerson - Inside REDIPS.drag event handlers you can reference "td" property. Here is list from documentation:

    td.source - reference to source table cell (set in onmousedown)
    td.previous - reference to previous table cell (set in onmousemove and autoscroll)
    td.current - reference to current table cell (set in onmousemove and autoscroll)
    td.target - reference to target table cell (target table cell is set in a moment of dropping element to the table cell)

    From this info it's possible to get DIV position and prepare data for saving to database.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

In case of posting HTML tags or JavaScript code please convert special characters to HTML entities.
Especially pay attention to convert "<" character to "&lt;" entity!