Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JQuery sortable nested sortable divs

This question has something to do with this one Nest jQuery UI sortables, but i couldn't solve my problem with it.

Here's the problem: I have a main container that contains items, those items are divs that can be ungrouped items or groups, that contain other items. I can define new groups by dragging the .multiply-group div and then I can drag all the group at once. Here's the code:

    <body>
    <div class="multiply-container">
        <div class="row multiply">Item 1</div>
        <div class="row multiply">Item 2</div>
        <div class="row multiply">Item 3</div>
        <div class="row multiply-group"> Group 1</div>
        <div class="row multiply">Item 4</div>
        <div class="row multiply-group"> Group 2 </div>
        <div class="row multiply">Item 5</div>
    </div>

    <script>

        var groupWrap = function(){

            $('.multiply-container').children().each(function(index, item){
                if($(item).hasClass('multiply-group')){
                    $(item).nextUntil('.multiply-group').addBack().wrapAll('<div class="locked"></div>');
                }
            });

        };

        var updateMultiply = function(){

            var $container = $('.multiply-container');

            $container.children().each(function(index, item){
                if($(item).hasClass('locked')){
                    $(item).children().each(function(i, elem){

                        $container.append($(elem));

                    });
                    $(item).remove();
                }


            });

            groupWrap();

            $('.multiply-container').sortable({
                connectWith: '.locked',
                helper: 'clone',
                placeholder: '.multiply-placeholder',
                axis: 'y',
                update: function(){
                    updateMultiply();
                }
            });

            $('.locked').sortable({
                connectWith: '.multiply-container, .locked',
                helper: 'clone',
                axis: 'y',
                update: function(){
                    updateMultiply();
                },
                receive: function(event, ui){

                    if($(ui.item).hasClass('locked')){
                        $(ui.sender).sortable('cancel');

                        return false;
                    }

                }
            });

        };




        updateMultiply();

    </script>

</body>

And here's a fiddle: https://jsfiddle.net/antoq/n1w6e6ar/2/

The problem is that I get is when I drag the last group container out of the bottom of the main container it stops down there instead of getting back to main container and I get the following error:

Uncaught HierarchyRequestError: Failed to execute 'insertBefore' on 'Node': The new child element contains the parent.

Can someone please help me understand what is going on and how to solve it?

like image 276
António Quadrado Avatar asked Feb 05 '16 19:02

António Quadrado


1 Answers

Maybe you were overthinking it when you coded that: two sortables, unnecessary canceling of events, connecting lists that were already connected, etc.

The issue with the last group getting stuck at the bottom or disappearing seemed to be an issue of re-attaching .sortable() at every list update, a recurrence which made for unexpected behaviour. But simply removing that recurrence made your list not behave as you seemingly intended it to, so some additional refactoring was necessary:

1) Use only one .sortable() call, then define which items will be sortable (namely .row and .locked for individual and grouped sorting, respectively). That is already enough for what you intended. The only problem here is that dragging a group into the middle of another group added another nesting level;

2) To prevent additional nesting levels, unwrap the .locked groups before wrapping them again.

var groupWrap = function() {
  $('.locked').children().unwrap();
  $('.multiply-container')
    .children().each(function(index, item) {
      if ($(item).hasClass('multiply-group')) {
        $(item).nextUntil('.multiply-group').addBack().wrapAll('<div class="locked"></div>');
      }
    });
};

var updateMultiply = function() {

  var $container = $('.multiply-container');

  $container.children().each(function(index, item) {
    if ($(item).hasClass('locked')) {
      $(item).children().each(function(i, elem) {

        $container.append($(elem));

      });
      $(item).remove();
    }
  });
};

$('.multiply-container').sortable({
  items: '.row, .locked',
  helper: 'clone',
  axis: 'y',
  update: function() {
    update();
  }
});

var update = function() {
  updateMultiply();
  groupWrap();
};

update();
body {
   background-color: #eee;
   padding: 50px;
 }
 .multiply {
   height: 45px;
   width: 100%;
   background-color: violet;
   border: 1px solid purple;
   margin: 0 auto;
   text-align: center;
 }
 .multiply-group {
   height: 25px;
   width: 100%;
   background-color: teal;
   border: 2px solid orange;
   margin: 0 auto;
   text-align: center;
 }
 .multiply-container {
   background-color: lime;
   padding: 20px;
 }
 .multiply-placeholder {
   height: 65px;
   width: 100%;
 }
 .locked {
   padding: 20px;
   background-color: cyan;
   border: 1px solid blue;
 }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"></script>
<body>
  <div class="multiply-container">
    <div class="row multiply">Item 1</div>
    <div class="row multiply">Item 2</div>
    <div class="row multiply">Item 3</div>
    <div class="row multiply-group">Group 1</div>
    <div class="row multiply">Item 4</div>
    <div class="row multiply-group">Group 2</div>
    <div class="row multiply">Item 5</div>
  </div>
</body>
like image 65
Tomas Buteler Avatar answered Oct 18 '22 17:10

Tomas Buteler