web-dev-qa-db-ja.com

jQueryドロップ可能-(最初のドラッグオーバーだけでなく)ドラッグオーバー中にイベントを受け取る

jQuery droppablejQuery draggable と組み合わせて)を使用して、ユーザーがリストからアイテムをドラッグしてテーブルにドロップすることにより、HTMLテーブルに行を追加できるようにしています。

これはうまく機能しますが、現在のロジックでは、ユーザーがテーブルの行にドラッグアンドドロップすると、新しい行が追加されますユーザーがドロップした行。

新しい行の追加位置が、ユーザーが既存の行の上半分にドロップしたか下半分にドロップしたかに基づいている場合は、より良いでしょう。

これはdropイベントで計算するのに十分簡単ですが、ユーザーがドラッグするときにUIフィードバックを提供する必要があります(2つのCSSクラスを使用してdroppable-aboveおよびdroppable-below 例えば)。

overイベントは一度だけ発生するため、これは可能ではないようです。ユーザーが最初にドロップ可能な要素の上にドラッグしたとき。

ユーザーがドロップ可能な要素上にあるときにマウスを動かすたびにoverイベントを発生させることは可能ですか?

もしそうなら、私はこれを行うことができるでしょう:

$("tr.droppable").droppable({
    over: function(event, ui) {
        if (/* mouse is in top half of row */) {
            $(this).addClass("droppable-above").removeClass("droppable-below");
        }
        else {
            $(this).removeClass("droppable-above").addClass("droppable-below");
        }
    },

    out: function(event, ui) {
        $(this).removeClass("droppable-above").removeClass("droppable-below");
    },

    drop: function(event, ui) {
        $(this).removeClass("droppable-above").removeClass("droppable-below");
        if (/* mouse is in top half of row */) {
            // Add new row above the dropped row
        }
        else {
            // Add new row below the dropped row
        }
    }
});

CSSスタイルは次のようになります...

droppable-above { border-top: solid 3px Blue; }
droppable-below { border-bottom: solid 3px Blue; }
24
Richard Ev

あなたが言ったように、over(対応するoutのように)はドロップ可能オブジェクトで一度だけ発生します。一方、draggabledragイベントは、マウスが動くたびに発生し、タスクに適しているようです。ただし、この戦略には2つの問題があります。

  • dragは、ドラッグ可能オブジェクトが実際にドロップ可能オブジェクトの上にあるかどうかに関係なく発生します。
  • その場合でも、ドロップ可能オブジェクトはイベントハンドラーに渡されません。

両方の問題を解決する1つの方法は、jQueryの data() 機能を使用してoverハンドラーでドロップ可能オブジェクトとドラッグ可能オブジェクトを関連付け、outおよびdropハンドラー:

$("tr.droppable").droppable({
    over: function(event, ui) {
        if (/* mouse is in top half of row */) {
            $(this).removeClass("droppable-below")
                   .addClass("droppable-above");
        }
        else {
            $(this).removeClass("droppable-above")
                   .addClass("droppable-below");
        }
        ui.draggable.data("current-droppable", $(this));  // Associate.
    },

    out: function(event, ui) {
        ui.draggable.removeData("current-droppable");     // Break association.
        $(this).removeClass("droppable-above droppable-below");
    },

    drop: function(event, ui) {
        ui.draggable.removeData("current-droppable");     // Break association.
        $(this).removeClass("droppable-above droppable-below");
        if (/* mouse is in top half of row */) {
            // Add new row above the dropped row.
        }
        else {
            // Add new row below the dropped row.
        }
    }
});

ドラッグ可能オブジェクトがその上にあるドロップ可能オブジェクトを認識したので、dragイベントハンドラーで要素の外観を更新できます。

$(".draggable").draggable({
    drag: function(event, ui) {
        var $droppable = $(this).data("current-droppable");
        if ($droppable) {
            if (/* mouse is in top half of row */) {
                $droppable.removeClass("droppable-below")
                          .addClass("droppable-above");
            } else {
                $droppable.removeClass("droppable-above")
                          .addClass("droppable-below");
            }
        }
    }
});

次のコードは、このソリューションを示す簡単なテストケースです(基本的に、上記のコメント付きのギャップを埋め、一般的なパターンをヘルパー関数にリファクタリングします)。ドロップ可能設定は、前の例よりも少し複雑です。主に、新しく作成されたテーブル行を兄弟のようにドロップ可能にする必要があるためです。

結果は this fiddle で確認できます。

HTML:

<div class="draggable">New item 1</div>
<div class="draggable">New item 2</div>
<div class="draggable">New item 3</div>
<div class="draggable">New item 4</div>
<div class="draggable">New item 5</div>
<p>Drag the items above into the table below.</p>
<table>
    <tr class="droppable"><td>Item 1</td></tr>
    <tr class="droppable"><td>Item 2</td></tr>
    <tr class="droppable"><td>Item 3</td></tr>
    <tr class="droppable"><td>Item 4</td></tr>
    <tr class="droppable"><td>Item 5</td></tr>
</table>

CSS:

p {
    line-height: 32px;
}

table {
    width: 100%;
}

.draggable {
    background-color: #d0ffff;
    border: 1px solid black;
    cursor: pointer;
    padding: 6px;
}

.droppable {
    background-color: #ffffd0;
    border: 1px solid black;
}

.droppable td {
    padding: 10px;
}

.droppable-above {
    border-top: 3px solid blue;
}

.droppable-below {
    border-bottom: 3px solid blue;
}

Javascript:

$(document).ready(function() {
    $(".draggable").draggable({
        helper: "clone",
        drag: function(event, ui) {
            var $droppable = $(this).data("current-droppable");
            if ($droppable) {
                updateHighlight(ui, $droppable);
            }
        }
    });

    initDroppable($(".droppable"));

    function initDroppable($elements)
    {
        $elements.droppable({
            over: function(event, ui) {
                var $this = $(this);
                updateHighlight(ui, $this);
                ui.draggable.data("current-droppable", $this);
            },
            out: function(event, ui) {
                cleanupHighlight(ui, $(this));
            },
            drop: function(event, ui) {
                var $this = $(this);
                cleanupHighlight(ui, $this);
                var $new = $this.clone().children("td:first")
                                .html(ui.draggable.html()).end();
                if (isInUpperHalf(ui, $this)) {
                    $new.insertBefore(this);
                } else {
                    $new.insertAfter(this);
                }
                initDroppable($new);
            }
        });
    }

    function isInUpperHalf(ui, $droppable)
    {
        var $draggable = ui.draggable || ui.helper;
        return (ui.offset.top + $draggable.outerHeight() / 2
                <= $droppable.offset().top + $droppable.outerHeight() / 2);
    }

    function updateHighlight(ui, $droppable)
    {
        if (isInUpperHalf(ui, $droppable)) {
            $droppable.removeClass("droppable-below")
                      .addClass("droppable-above");
        } else {
            $droppable.removeClass("droppable-above")
                      .addClass("droppable-below");
        }
    }

    function cleanupHighlight(ui, $droppable)
    {
        ui.draggable.removeData("current-droppable");
        $droppable.removeClass("droppable-above droppable-below");
    }
});
30

私は同じ問題を抱えており、この比較的まれなニーズを見つけた他の人に指示を与える場合に備えて共有する2つのソリューションについて考えています。

  • 2つのdivソリューション:行の各セルに2つのdivを追加します。UIの干渉から保護するために、スタックされるように配置され、高さと幅がそれぞれ50%で、zインデックスが-1に設定されています。次に、これらのドロップ可能オブジェクトを作成し、それらの「over」および「out」イベントを使用して、親セルまたは行のクラスを切り替えます。

  • ドロップ可能を破棄し、独自の衝突検出をロールする:ドロップ可能効果を模倣する独自の衝突検出を記述します。これはより多くの自由を与えますが、いくつかの深刻なコーディングにつながるため、カジュアルな要件ではありません。また、パフォーマンスの問題の影響を受けやすくなります。とはいえ、あなたの好みで機能する明らかなケースベースのショートカットがいくつかあるはずです。

ローコードソリューションへの他のアプローチについて聞いてみたいと思います。

2
James Evason

別の方法は、クラスまたは他の選挙人をヒント要素に追加することです。次に、ドラッグ可能な定義のドラッグハンドラーで、ヒントの位置を更新します。

$('#dropArea').droppable({
        over: function(event, ui) 
            // Create a clone 50 pixels above and 100 to the left of drop area 
            $('#someHint').clone()
                    .css({
                        position: 'absolute',
                        top: ui.offset.top+50,
                        left: ui.offset.left-50
                    })
                    .addClass("clone")          // Mark this as a clone, for hiding on drop or out
                    .addClass("dragHelper")     // Mark this as a hint, for moving with drag
                    .appendTo(document.body)


        },
        out: function(event, ui) {
            $('.clone').remove();                       // Remove any hints showing
        },
        drop: function(event, ui) {
            $('.clone').remove();                       // Remove any hints showing
        }
    });

$("#mydiv").draggable({
        drag: function(event, ui) {
            $('.dragHelper').css('left',ui.offset.left);
            $('.dragHelper').css('top',ui.offset.top);
        }

});
1
NoelHunter

私はこの問題にぶつかった。 「ドラッグ」イベントでヒットテストを実装するだけの場合、それほど多くは必要ありません。ここでは、すべてのドロップターゲットに.myDropTargetのタグを付けたので、それらすべてを簡単に見つけてループし、マウスがそれらの上にあるかどうかを確認できます。

このようなもの:

thingToBeDragged.draggable({
    drag: function(evt) {

        $('.myDropTarget').removeClass('topHalf bottomHalf').each(function() {
            var target = $(this), o = target.offset(),
                x = evt.pageX - o.left, y = evt.pageY - o.top;

            if (x > 0 && y > 0 && x < target.width() && y < target.height()) {

                // mouse is over this drop target, but now you can get more
                // particular: is it in the top half, bottom half, etc.

                if (y > target.height() * 0.5) {
                    target.addClass('topHalf');
                } else {
                    target.addClass('bottomHalf');
                }
            }
        });
    },
    stop: function() {
        var droppedOn = $('.topHalf, .bottomHalf');
    }
});
1

これはかなり大雑把な(そしてコードレスな)ソリューションですが、DroppableでhoverClassオプションを使用し、「hovering」という新しいクラスを作成して、DraggableがDroppableの上にホバーしているときにのみ発生するDroppable動作を設定できます。この「ホバリング」クラスは、(これは粗雑なビットです)ある種のエンドレスループまたは他の種類のチェッカーを実行できます。私はこれらのクラスを以前に使用したことがないので、この時点を過ぎてこれ以上の詳細を考えることはできません。 = /

編集:さらに大まかに言えば、「ホバリング」クラスを使用して、Droppableを無効化および有効化することもできます。私は間違いなくを同期的に行いますが、寛大な時間の描写も行います。 disableとenableを交互に呼び出すすべきイベントの1つをトリガーしますが、それを調べるには実験する必要があります。

0
sichinumi