Link to home
Start Free TrialLog in
Avatar of ROM
ROMFlag for United Kingdom of Great Britain and Northern Ireland

asked on

JQuery Multisortable.. does not work on multi selected Divs.. Fix or Different Drag and Drop

Having issues with JQuery multisortable.


Hey everyone.. Been using multisortable for this year and just not happy with it.

It has some update issues on the multiselect.


And when you try to resort multiple DIVs within the same DIV list the additional DIVs are always ignored. It does not catch them.


I have put a mock up together in the code section. Left to Right divs... seems to work ok.. although the order of updates are a bit odd. But I can live with that as it works for me.

<html>
    <head>     <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">     <script src="https://code.jquery.com/jquery-3.6.0.js"></script>      <script src="https://code.jquery.com/ui/1.13.0/jquery-ui.js"></script>     <script src="media/js/ms/src/jquery.multisortable.js"></script>     </script>     </head>     <style>         #pagewrapper {             display: flex;         }         #left {             width: 50%;             border: 1px solid red;         }                 #right {             width: 50%;             border: 1px solid green;         }              .selected {             border: 5px solid pink;         }                          </style> <body> <h1>Test<h1> <div id="pagewrapper">     LEFT div     <div id="left" class="connectedSortable" data-hid="left">         <div id="1" class="box" data-index="1" data-position="1">My div 1</div>         <div id="2" class="box" data-index="2" data-position="2">My div 2</div>         <div id="3" class="box" data-index="3" data-position="3">My div 3</div>         <div id="4" class="box" data-index="4" data-position="4">My div 4</div>         <div id="5" class="box" data-index="5" data-position="5">My div 5</div>         <div id="6" class="box" data-index="6" data-position="6">My div 6</div>     </div>     RIGHT div     <div id="Right" class="connectedSortable" data-hid="right">     </div> </div> </body> <script> $('#pagewrapper').ready(function() {     }); $('#pagewrapper').on('click', '.box', function MyDragnDrop() {     $( ".connectedSortable" ).multisortable({items: "div.box", connectWith: ".connectedSortable",selectedClass:"selected",           update: function(event) {         console.log("event: "+event.target.id);                         $(this).children().each(function (index) {                                                         var myindexdata = $(this).attr('data-index');                             parentdiv = $(this).parent().attr('data-hid');                             index = index+1;                                     $(this).attr('data-position', (index)).addClass('updated');                                    console.log("My Div: "+myindexdata+ " Index: "+index+ " data-position: "+$(this).attr('data-position')+ " parentdiv: " + parentdiv);                         });                         //   Run my AJAX function with an array I build from the above just catching the changes etc...                                     }     }); }); </script> </html>

Open in new window


HOWEVER, when you resort a multiselected group of DIVs within the same it never registers the additional selected DIVs and therefore I do not get my AJAX fired to update their positions which of course I need.


On the Left div just try moving 3 and 4 to the end for example. You will see the 3 Div gets registered but 4 is ignored so the event never occurs that I can catch and then fire to Ajax so my list is always wrong.


a) Any ideas on how to FIX the multisortable? 

b) Different framework with same ability easy to implement into my existing projects which is maybe more popular?


Many thanks in advance


R

Avatar of ROM
ROM
Flag of United Kingdom of Great Britain and Northern Ireland image

ASKER

When I iterate through the parent div LEFT... after the event via a click somewhere else.. all displays correct.

But as part of that UPDATE event I cannot get the additional dragged divs.

Many thanks in advance

R
Avatar of ROM

ASKER

Also tried monitoring for a change in the div:

$('#left').bind('contentchanged', function() {
  // do something after the div content has changed
  rerunme();
})



Open in new window

Rerunme just runs an iteration and reports.

Again does not work.. ONLY works on a separate click event if I click to iterate through the children of the #left div.

Totally stuck.

Many thanks in advance

R
Avatar of Michel Plungjan
I found
 <script   src="https://rawgithub.com/shvetsgroup/jquery.multisortable/master/src/jquery.multisortable.js"></script>

and made

https://jsfiddle.net/mplungjan/up8dvzqm/

Can you tell us exactly the steps you take to see the issue
Avatar of ROM

ASKER

Hi Michel,

Put CONSOLE on.

Then drag 3 and 4 simultaneously from LEFT to RIGHT.

You will see the update event does NOT register the additional div 4.. however the RIGHT div corrects this by reporting 3 and 4.... as part of its update event. This means the end result is ok..

Reset.. Put console on.

Then drag 3 and 4 within LEFT div to beneath 6.. You will see in the CONSOLE it does not register the 4 div. This is NOT corrected like above as there are no other events for other divs firing so it means the order is always wrong when trying to do an ajax update.

Many thanks in advance

R
Avatar of ROM

ASKER

You have Right instead of right as the div id.

Just correct that..

Thanks

R
I get

"event: left"
"My Div: 1 Index: 1 data-position: 1 parentdiv: left"
"My Div: 2 Index: 2 data-position: 2 parentdiv: left"
"My Div: 3 Index: 3 data-position: 3 parentdiv: left"
"My Div: 5 Index: 4 data-position: 4 parentdiv: left"
"My Div: 6 Index: 5 data-position: 5 parentdiv: left"
"My Div: 4 Index: 6 data-position: 6 parentdiv: left"

Open in new window


in Chrome. What is wrong with that?

You want this instead?

"event: left"
"My Div: 1 Index: 1 data-position: 1 parentdiv: left"
"My Div: 2 Index: 2 data-position: 2 parentdiv: left"
"My Div: 5 Index: 4 data-position: 4 parentdiv: left"
"My Div: 6 Index: 5 data-position: 5 parentdiv: left"
"My Div: 3 Index: 3 data-position: 3 parentdiv: left"
"My Div: 4 Index: 6 data-position: 6 parentdiv: left"

Open in new window


DOM shows

<div id="left" class="connectedSortable ui-sortable" data-hid="left">
    <div id="1" class="box ui-sortable-handle updated selected multiselectable-previous" data-index="1" data-position="1">My div 1</div>
    <div id="2" class="box ui-sortable-handle updated" data-index="2" data-position="2">My div 2</div>
    <div id="5" class="box ui-sortable-handle updated" data-index="5" data-position="4">My div 5</div>
    <div id="6" class="box ui-sortable-handle updated" data-index="6" data-position="5">My div 6</div>
    <div id="3" class="box ui-sortable-handle updated" data-index="3" data-position="3" style="">My div 3</div>
    <div id="4" class="box ui-sortable-handle updated" data-index="4" data-position="6" style="">My div 4</div>
  </div>

Open in new window

Avatar of ROM

ASKER

Yes correct.

The values you are getting are wrong.

The divs are ordered:
1,2,5,6,3,4 and as such I need to do an ajax update to save to the database the new positions.

Just like the values that you get... mine are 1,2,4,5,6,3
This is totally incorrect and therefore means the ordering and position cannot be updated correctly rendering the multiple drag and drop to resort completely useless.

I find the same thing happens even if I monitor the #left for changes.. It is only when I click and prompt to iterate through the children of #left on a click afterwards or on a timer does this work.

But of course I need this to occur as part of the update. I am unsure if it is the framework at fault, how the DOM changes or even my understanding.

Many thanks in advance.

R
Avatar of ROM

ASKER

The DOM looks ok.. Hence my click later.

But I need the update event to be correct so my ajax database position update scripts work and update accordingly.
Right now they are only updating for Div 3 as per my example.

So I need to capture that order as part of the update event.

Many thanks

R
ASKER CERTIFIED SOLUTION
Avatar of Michel Plungjan
Michel Plungjan
Flag of Denmark image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
try deactivate event instead update
@Michel :
User generated image
Ah, -  I used sortdeactivate  -  so I changed it to deactivate and it does not work

https://jsfiddle.net/mplungjan/913pch0a/

See the order

mine (using stop) works

https://jsfiddle.net/mplungjan/up8dvzqm/
Avatar of ROM

ASKER

OK thank you Michel and Leak.

Leak I tried deactivate but it still returned the wrong order.

I can see this is working with stop Michel.

Just trying to work out now the best way to capture this DIV and then process all the positions so I can then run an ajax update

many thanks in advance.

R
Avatar of ROM

ASKER

I say capture the DIV as the production version of this has many many divs.

I.e. Div1, Div2, Div3, Div4, Div5 and on and on.

So I need to capture the DIV that has just been updated and then run some code to get the children and the index so I can then run an ajax update.

Many thanks

R
You can compare the old array with the new array
Avatar of ROM

ASKER

Yes but I must CAPTURE which DIV has actually been updated so I can iterate through the children to get their information.

I cannot static set #left .. I must dynamic understand this value from the very last drag and drop.

Many thanks in advance

R
Avatar of ROM

ASKER

OK I have got it.
At the end of the UPDATE statement I store a variable LASTDIV with the parent div of the dropped box divs.

Then on the STOP statement I call my AJAX/OTHER function using the LASTDIV as a parameter.
OK cool.. I think I can sort this out now.

Many thanks in advance

R
You could check in click

click: function(e) { console.log(e.target.id)},

Open in new window

Avatar of ROM

ASKER

yes I am doing that.. but I cannot do that on the STOP event as the div that activated this is the dropped div.

I need to store in a variable from the UPDATE event as the event that triggered it is the LEFT div in our example

Thanks

R
Avatar of ROM

ASKER

OK so here is my final version. It does run too many AJAX calls as we are doubling up the effort.

a) Dragging from one div to another.. All ok... Don't need the extra.
b) Dragging within a div - This is when this is needed.

So
<html>
    <head>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
    <script src="https://code.jquery.com/jquery-3.6.0.js"></script> 
    <script src="https://code.jquery.com/ui/1.13.0/jquery-ui.js"></script>
    <script src="media/js/ms/src/jquery.multisortable.js"></script>
    </script>
    </head>
    <style>
        #pagewrapper {
            display: flex;
        }

        #left {
            width: 50%;
            border: 1px solid red;
        }        
        #right {
            width: 50%;
            border: 1px solid green;
        }     
        .selected {
            border: 5px solid pink;
        }                     

    </style>
<body>
<h1>Test<h1>

<div id="pagewrapper">
<div id="testbut1">testbut1</div>
    LEFT div
    <div id="left" class="connectedSortable" data-hid="left">
        <div id="1" class="box" data-index="1" data-position="1">My div 1</div>
        <div id="2" class="box" data-index="2" data-position="2">My div 2</div>
        <div id="3" class="box" data-index="3" data-position="3">My div 3</div>
        <div id="4" class="box" data-index="4" data-position="4">My div 4</div>
        <div id="5" class="box" data-index="5" data-position="5">My div 5</div>
        <div id="6" class="box" data-index="6" data-position="6">My div 6</div>
    </div>
    RIGHT div
    <div id="right" class="connectedSortable" data-hid="right">

    </div>
</div>

    </div>

</body>
<script>

$('#pagewrapper').ready(function() {    
});

let lastdiv = '';
let clickdiv = '';

$('#pagewrapper').on('click', '.box', function MyDragnDrop() { $( ".connectedSortable" ).multisortable({items: "div.box", connectWith: ".connectedSortable",selectedClass:"selected", 
        
        click: function(e,item) {
            clickdiv = $(item).parent().attr('data-hid');
        },
        stop: function(e){

            if (lastdiv.length > 0)
                {
                    rerunme(lastdiv,clickdiv);
                }            
        },
         update: function(event) {
                        $(this).children().each(function (index) {   
                                                   
                            var myindexdata = $(this).attr('data-index');
                            parentdiv = $(this).parent().attr('data-hid');
                            index = index+1;
                                    $(this).attr('data-position', (index)).addClass('updated');
                                   console.log("My Div: "+myindexdata+ " Index: "+index+ " data-position: "+$(this).attr('data-position')+ " parentdiv: " + parentdiv);
                                    lastdiv = parentdiv;        
                                    console.log("parentdiv: "+ parentdiv);                            
                        });

                        // Run some AJAX position scripts.

        }

    });
});


function rerunme(mydiv,mydiv2){
    $('#'+mydiv).children().each(function (index) {  
               var myindexdata = $(this).attr('data-index');
               parentdiv = $(this).parent().attr('data-hid');
               
               if (mydiv == mydiv2)
                {
                    console.log("rerunme - My Div: "+myindexdata+ " Index: "+index+ " data-position: "+$(this).attr('data-position')+ " parentdiv: " + parentdiv);    
                    //   Run my AJAX function with an array I build from the above just catching the changes etc...                
                }
               else
                {
                    console.log("They are different - DO NOT RUN");
                }

                lastdiv = '';
                clickdiv = '';                

                        
                    });

}



</script>


</html>



Open in new window

Many thanks for your help.

Anyway you would improve this? Disappointed I am running 2x lots of AJAX updates unnecessarily just because I am cleaning up afterwards.

Many thanks

R
Avatar of ROM

ASKER

I could look for SAME DIV .. run this.. or DIFF DIV run that... this would reduce ajax updates.

NOPE it would not.. as I still need original AJAX running for when you drop between Divs.. unless I run completely different there and utilise the RECEIVE: statement.

That is my only thoughts on improvement.

R
I cannot figure out what you NEED to do and what the script does NOT do for you

Here is the running example of your latest code

https://jsfiddle.net/mplungjan/y2pze1nh/

What SHOULD it do and how is it failing?

What is rerunme supposed to do that it is not
Avatar of ROM

ASKER

Hi Michel,

For clarity.. my latest version of the code.. is ALL GOOD. I have implemented already into my development project. I am just asking if any improvements?

So ---

Rerunme is iterating through the LASTDIV that had an event... so this would be the DIV that had the order change.
It then collects the box DIV ID and INDEX number... and then does an AJAX update meaning that when the order is incorrect for multiple divs being reordered it corrects it.

First go with the UPDATE event I would receive 1,2,4,5,6,3

This is wrong.. the DOM is right.. but the events are wrong.

Rerunme then reprocesses (and sends an ajax db update) with the order 1,2,5,6,3,4

This means my database is up to date and reflects the DOM.

It is just a shame that I have to run 2x AJAX updates to correct a reorder within the same DIV.

But I can live with this. I just wanted to know if there were any obvious improvements.

Many thanks

R



stop: function(event) { // stop or update should work with a timer
    setTimeout(function() { // the ugly way
       // do your ajax here
    }, 100); // wait 100ms, so you got the DOM right
}

Open in new window

Why would we need a timer. In my test, stop gets the DOM correctly immediately
Why would we need a timer. In my test, stop gets the DOM correctly immediately
@Michel, you right, I see stop event is fired after DOM fully updated.

@Romolo, my two cents :
Don't do too much stuff on client side, I would just use stop event, loop over to get id to build and array and send this array to server, for example : [1,2,5,6,3,4]
If needed, server code could be 1 000 000 lines of code to update database... if needed...
I would keep client side as cleanest as possible.

<html>
<head>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<style>
#pagewrapper {
    display: flex;
}
#left {
    width: 50%;
    border: 1px solid red;
}
#right {
    width: 50%;
    border: 1px solid green;
}
.selected {
    border: 5px solid pink;
}
</style>
</head>
<body>
<h1>Test</h1>
<div id="pagewrapper">
    <div id="testbut1">testbut1</div>
    LEFT div
    <div id="left" class="connectedSortable" data-hid="left">
        <div id="1" class="box" data-index="1" data-position="1">My div 1</div>
        <div id="2" class="box" data-index="2" data-position="2">My div 2</div>
        <div id="3" class="box" data-index="3" data-position="3">My div 3</div>
        <div id="4" class="box" data-index="4" data-position="4">My div 4</div>
        <div id="5" class="box" data-index="5" data-position="5">My div 5</div>
        <div id="6" class="box" data-index="6" data-position="6">My div 6</div>
    </div>
    RIGHT div
    <div id="right" class="connectedSortable" data-hid="right"></div>
</div>
<script src="//code.jquery.com/jquery-3.6.0.js"></script>
<script src="//code.jquery.com/ui/1.13.0/jquery-ui.js"></script>
<script src="//rawgit.com/shvetsgroup/jquery.multisortable/master/src/jquery.multisortable.js"></script>
<script>
    $('#pagewrapper').ready(function() {
    }).on('click', '.box', function MyDragnDrop() {
        $( ".connectedSortable" ).multisortable({
            items: "div.box",
            connectWith: ".connectedSortable",
            selectedClass: "selected",
            stop: function(e) {
                //console.log($(e.target).html());
                $.post("/path/to/save/box/order",{ array_Id_new_order : $("div.box",e.target).map((i,box)=>box.id).toArray() });
            },
        });
    });
</script>
</body>
</html>

Open in new window


Very strange construct

$("div.box",e.target).map((i,box)=>box.id).toArray()

Open in new window

Why not .get()  ?
Avatar of ROM

ASKER

@leakim971 .. Yes I am doing that now.. I am posting positions via ajax db update. All I am doing is building an array to pass over to a PHP file.

@michel .. STOP seems to work so without over complicating I will just stick with this for now rather than reduce down the ajax calls at least for this dev copy and evaluate.

Many thanks for all the help


R
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial