Home > Back-end >  Dynamically disabling jQuery UI sortable elements
Dynamically disabling jQuery UI sortable elements

Time:01-14

I have a number of jQuery UI sortable parent elements. Each parent element can contain a maximum of one droppable child element so:

  • If a container is empty, it should be able to receive a droppable element from any other container
  • If a container is not empty:
    • It should not be able to receive a droppable element from another container
    • But the current child should be droppable to any other empty parent element

To help facilitate this the parent elements all have the class drop_target_cell and empty parent elements have the class empty_drop_target_cell. These are updated as droppable elements are allocated.

To achieve this I've tried disabling any elements with the class drop_target_cell but not the class empty_drop_target_cell when the user starts dragging a droppable element, and then reenabling all elements with the class drop_target_cell when the drag operation finishes. However this does not work as I expected; it is still possible to drag droppable elements into parent containers which already have a child element.

Below is my best effort, with added background colour changes which are (correctly) indicating which parent elements should be disabled when dragging starts:

$("#available_sections, td.drop_target_cell").sortable({
    connectWith: "#available_sections, td.drop_target_cell",
    start: function (event, ui) {
        $("td.drop_target_cell:not(td.drop_target_cell.empty_drop_target_cell)").css('background-color', '#ff00ff');
        $("td.drop_target_cell:not(td.drop_target_cell.empty_drop_target_cell)").sortable("disable");
    },
    stop: function (event, ui) {
        $("td.drop_target_cell").css('background', 'none');
        $("td.drop_target_cell").sortable("enable");
    }
});
$("td.drop_target_cell").on("sortremove", function (event, ui) {
    $(this).addClass('empty_drop_target_cell');
});
$("td.drop_target_cell").on("sortreceive", function (event, ui) {
    $(this).removeClass('empty_drop_target_cell');
});

Is there a better approach, or is there something I can change to get this working?

CodePudding user response:

I managed to get this working with the code below. All drop targets have the class drop_target_cell and populated drop targets (which should only allow items to be removed) have the class populated_drop_target_cell.

Two important learnings for anyone that stumbles on this:

  • For changes to take effect you must call $(this).sortable("refresh") after your change.

  • Any position css (fixed,relative etc.) somewhere in the ancestry of your sortable list elements may interfere with the sortable. If anything isn't working as expected and you can't find the cause, this may be it. See also this question (this had me scratching my head for hours).

    $("td.drop_target_cell").sortable({
        connectWith: "td.drop_target_cell",
        start: function (event, ui) {
            $("td.drop_target_cell.populated_drop_target_cell").sortable("option", "disabled", true);
            $(this).sortable("refresh");
        },
        stop: function (event, ui) {
            $("td.drop_target_cell").sortable("option", "disabled", false);
            $(this).sortable("refresh");
        }
    });
    
  •  Tags:  
  • Related