jOrgChart
jOrgChart copied to clipboard
Dragdrop doesn't work with latest jquery ui
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'
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');
Came here to post the same thing. One line change and now it works with jQuery 2.0.0 and UI 1.10.2
A+ on that last change, thanks for saving me some digging.
+1, saved my life :)
Thanks deJong-IT - this fix works a treat.
@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);
});
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.
@svoboda-jan Thank you!
@svoboda-jan Thank you! +1
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:
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"> </td><td class="left"> </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"> </td><td class="right top"> </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();
}
});
Excellent thanks!