wesnolte / jOrgChart

A jQuery plugin to draw tree-like structures such as OrgCharts.
992 stars 674 forks source link

Dragdrop doesn't work with latest jquery ui #32

Open deJong-IT opened 11 years ago

deJong-IT commented 11 years ago

When using jquery 1.8.3 and jqueryui 1.9.2 you'll get this error when you try to drag:

Uncaught Error: cannot call methods on droppable prior to initialization; attempted to call method 'disable'

deJong-IT commented 11 years ago

I solved it

change this:

        sourceNode.parentsUntil('.node-container')
                .find('*')
                .filter('.node')
                .droppable('disable');

to:

        sourceNode.parentsUntil('.node-container')
                .find('*')
                .filter('.node:data(draggable)')
                .droppable('disable');
beardedlinuxgeek commented 11 years ago

Came here to post the same thing. One line change and now it works with jQuery 2.0.0 and UI 1.10.2

Saeven commented 10 years ago

A+ on that last change, thanks for saving me some digging.

jbonigomes commented 9 years ago

+1, saved my life :)

GarryLowther commented 9 years ago

Thanks deJong-IT - this fix works a treat.

DoDSoftware commented 9 years ago

@deJong-IT , This fix makes the error message go away but doesn't seem to resolve the issue.

The original function's purpose was to prevent users from dropping a parent element on one of its's own children by disabling all of the the child draggables when you start to drag a node.

For me, your change above prevents the error message on drag but it does not actually disable the child draggables. This means that if a user drops a parent node onto one of it's own children, an error will be thrown.

As an example, go to http://jsfiddle.net/DelightedDoD/kb9njsm5/3/ and drag "vegetables" onto "pumpkins"

I couldn't get the original functionality to work using disable. However, I was able to get everything working by setting a class on the child nodes and checking for that class in the accept function:

See working example at http://jsfiddle.net/DelightedDoD/kb9njsm5/4/

      $('div.node').droppable({
            accept: function(d) { 
               // if droppable was marked as a child of the draggable
                // reject the drop
               if( $(this).attr('class').match(/preventDrop /) ){ 
                  return false;
               }
               else if( $(d).attr('class').match(/node /)){ 
                  return true;
               }
            },          
            activeClass : 'drag-active',
            hoverClass  : 'drop-hover'
      });

      $('div.node').bind("dragstart", function handleDragStart( event, ui ){
        var sourceNode = $(this);
        sourceNode.parentsUntil('.node-container')
                .find('*')
                .filter('.node')
                .addClass('preventDrop'); // instead of disabling, mark all child nodes with a class
      });

      // Drag stop event handler for nodes
      $('div.node').bind("dragstop", function handleDragStop( event, ui ){
        $('.node').removeClass('preventDrop'); // remove all the child flags when dragging stops
        /* reload the plugin */
        $(opts.chartElement).children().remove();
        $this.jOrgChart(opts);      
      });
svoboda-jan commented 9 years ago

Just came across the same issue. The simplest fix maintaining the intended behaviour I found looking through jQuery UI source code and documentation is making the following change:

From:

// Drag start event handler for nodes
$('div.node').bind("dragstart", function handleDragStart( event, ui ){

  var sourceNode = $(this);
  sourceNode.parentsUntil('.node-container')
             .find('*')
             .filter('.node')
             .droppable('disable'); // <--- change this line
});

To:

// Drag start event handler for nodes
$('div.node').bind("dragstart", function handleDragStart( event, ui ){

  var sourceNode = $(this);
  sourceNode.parentsUntil('.node-container')
             .find('*')
             .filter('.node')
             .droppable({ disabled: true }); // <--- change this line
});

It initializes the droppable and disables it.

gmatta01 commented 9 years ago

@svoboda-jan Thank you!

saadhre commented 8 years ago

@svoboda-jan Thank you! +1

dabeng commented 8 years ago

Hi @deJong-IT @beardedlinuxgeek @Saeven @jbonigomes @TriSysBusinessSoftware , in my opinion, the less dependencies a plugin has, the more universal it become. So I recommend you OrgChart(derived from jOrgChart) which uses native HTML5 drag/drop API to accomplish the same task. This is demo. Good luck! :blush: screen-capture The followings are core source code for completing the above effect.

$nodeDiv.on('dragstart', function(event) {
  event.originalEvent.dataTransfer.setData('text/html', 'hack for firefox');
  var $dragged = $(this);
  var $draggedZone = $dragged.closest('table').find('.node');
  $dragged.closest('.orgchart')
    .data('dragged', $dragged)
    .find('.node').each(function(index, node) {
      if ($draggedZone.index(node) === -1) {
        $(node).addClass('allowedDrop');
      }
    });
})
.on('dragover', function(event) {
  event.preventDefault();
  var $dropZone = $(this);
  var $dragged = $dropZone.closest('.orgchart').data('dragged');
  if ($dragged.closest('table').find('.node').index($dropZone) > -1) {
    event.originalEvent.dataTransfer.dropEffect = 'none';
  }
})
.on('dragend', function(event) {
  $(this).closest('.orgchart').find('.allowedDrop').removeClass('allowedDrop');
})
.on('drop', function(event) {
  var $dropZone = $(this);
  var $orgchart = $dropZone.closest('.orgchart');
  var $dragged = $orgchart.data('dragged');
  $orgchart.find('.allowedDrop').removeClass('allowedDrop');
  var $dragZone = $dragged.closest('.nodes').siblings().eq(0).children();
  // firstly, deal with the hierarchy of drop zone
  if (!$dropZone.closest('tr').siblings().length) { // if the drop zone is a leaf node
    $dropZone.append('<i class="edge verticalEdge bottomEdge fa"></i>')
      .parent().attr('colspan', 2)
      .parent().after('<tr class="lines"><td colspan="2"><div class="down"></div></td></tr>'
      + '<tr class="lines"><td class="right">&nbsp;</td><td class="left">&nbsp;</td></tr>'
      + '<tr class="nodes"></tr>')
      .siblings(':last').append($dragged.find('.horizontalEdge').remove().end().closest('table').parent());
  } else {
    var dropColspan = parseInt($dropZone.parent().attr('colspan')) + 2;
    var horizontalEdges = '<i class="edge horizontalEdge rightEdge fa"></i><i class="edge horizontalEdge leftEdge fa"></i>';
    $dropZone.closest('tr').next().addBack().children().attr('colspan', dropColspan);
    if (!$dragged.find('.horizontalEdge').length) {
      $dragged.append(horizontalEdges);
    }
    $dropZone.closest('tr').siblings().eq(1).children(':last').before('<td class="left top">&nbsp;</td><td class="right top">&nbsp;</td>')
      .end().next().append($dragged.closest('table').parent());
    var $dropSibs = $dragged.closest('table').parent().siblings().find('.node:first');
    if ($dropSibs.length === 1) {
      $dropSibs.append(horizontalEdges);
    }
  }
  // secondly, deal with the hierarchy of dragged node
  var dragColspan = parseInt($dragZone.attr('colspan'));
  if (dragColspan > 2) {
    $dragZone.attr('colspan', dragColspan - 2)
      .parent().next().children().attr('colspan', dragColspan - 2)
      .end().next().children().slice(1, 3).remove();
    var $dragSibs = $dragZone.parent().siblings('.nodes').children().find('.node:first');
    if ($dragSibs.length ===1) {
      $dragSibs.find('.horizontalEdge').remove();
    }
  } else {
    $dragZone.removeAttr('colspan')
      .find('.bottomEdge').remove()
      .end().end().siblings().remove();
  }
});
JordiCorbilla commented 8 years ago

Excellent thanks!