Jquery UI draggable / droppable snap to grid not positioned correctly

I’ve been trying out JqueryUI for a simple task where I need to implement a basic drag and drop feature. I noticed that when I use the snap to grid option, the elements aren’t aligned properly when they are being dropped to the target container. It calculates the position of the draggable element from within its container although I defined the containment to be the target area.

see a sample code here: https://jsfiddle.net/9yzL87md/7/

 $( function() {
        $( ".spare-item" ).draggable({
            grid: [ 26, 26 ],
            containment: "#preview",
            cursor: "grab",
            revert: "invalid",
            start: function( event, ui ) {
                console.log(ui);
                console.log(ui);
            }                
        });
        $( "#preview" ).droppable({
                accept: ".spare-item",
                over: function(event, ui) {
                    $(".spare-item").draggable({
                        grid: [26, 26]
                    });
                },
                out: function(event, ui) {
                    $(".spare-item").draggable("option", "grid", false);
                },
        drop: function( event, ui ) {
            var pos = ui.draggable.offset(), dPos = $(this).offset();
            //console.log(pos.top - dPos.top, pos.left - dPos.left);
        }
        });            
    } ); 

above is a sample of my layout for easier understanding. If you try to drag the red element, it will immediately take that into the grey box, but will be snapped a little bit lower than the lower border. I tried many of the similar questions and solutions, but had no luck. What am I missing here? (the width of the container is 234px, and the width of red elements are 26px. I expect the red elements to be snapped in a 26 x 26 grid)

The issue your having is due to the padding and margin in the actions, item-container and the spare-item classes. Setting margin and padding to zero in these classes fixes the issue. e.g.

.actions{
  width: 234px;
  margin: 0px auto;
  padding: 0px; /*Updated*/
}

.item-container{
  height: 26px;
  margin: 0px auto; /*Updated*/
 }

.spare-item{
  background: red;
  display: inline-block;
  width: 26px;
  height: 26px;
  margin: 0px 0px; /*Updated*/
 } 

The jquery is not accounting for the padding and margin between the #preview and the actions item-container divs when calculating the position offsets which is causing it to snap to the grid with a top gap same as the gap between the 2 containers. So in order to fix this please see the section ‘Edit for Solution’ below:

[Edit for Solution]

Just to update for a solution to everyone on this. You need to use the snap option and set it’s value to the grid element within the item’s draggable function. e.g. in order to fix your fiddle just add this snap property in the spare-items draggable method options as follows:

        $( ".spare-item" ).draggable({
            grid: [ 26, 26 ],
            containment: "#preview",
            cursor: "grab",
            revert: "invalid",
            start: function( event, ui ) {
                console.log(ui);
                console.log(ui);
            },
            snap: '#preview'  // removes any margin or padding from the item
        });

Hope this helps you and all who are experiencing this issue.
Happy Coding. 🙂

you have multiple issues here. the draggable elements are still in the lower div, and affected by margins and paddings, which offsets them relative do your .item-container. finally, because they are inline-block elements (as opposed to block elements) the whitespace BETWEEN TAGS causes there to be an empty space between the elements. Either you turn them into block elements, or there cannot be any whitespace between the spare-item div tags.

The grid seems to be offset by the original item position, so offset spare-item’s have an offset grid.

Result: https://jsfiddle.net/9yzL87md/10/

  1. margins and paddings

neither .output, .actions or .item-container can have margins or paddings that affect the spare-items.

.output{
    background:#646464;
#    padding: 5px; removed
}

.actions{
    width: 234px;
    margin: 0px auto;
#    padding: 5px; removed
}

.item-container{
    height: 26px;
    margin: 0px auto; #no vertical
}

.spare-item{
    background: red;
    display: inline-block;
    width: 26px;
    height: 26px;
    margin: 0 0; # no horizontal margin
}
  1. inline-block whitespace

inline-block elements have a space between them if there is ANY whitespace at all (space, tab, newline). so either make the spare-item elements
display: block
or remove the whitespace between them with any variation of the following:

<div class="actions item-container"><!--
  --><div class="spare-item"></div><!--
  --><div class="spare-item"></div><!--
  --><div class="spare-item"></div><!--

–>

or one of the other tricks described here
https://css-tricks.com/fighting-the-space-between-inline-block-elements/

in case the link dies, the options listed are:

  • shifting the tags so there is no whitespace between divs
  • html comments between divs
  • negative margin on the item divs to counter
    whitespace
  • not putting closing tags because html5 allows it (havent
    tested this)
  • font-size: 0; on the item-container (didnt work for me)