24

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

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

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

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

overイベントは1回しか発生しないため、これは不可能のようです。ユーザーが最初にドロップ可能な要素をドラッグしたとき。

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; }
4

5 に答える 5

30

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

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

over両方の問題を解決する 1 つの方法は、jQuery のdata()機能を使用してハンドラーでドロップ可能とドラッグ可能を関連付け、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");
            }
        }
    }
});

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

この 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");
    }
});
于 2011-06-22T15:30:46.837 に答える
2

私は同じ問題に直面しており、この比較的まれな必要性を見つけた他の人に指示を与える場合に備えて共有する2つの解決策について考えています.

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

  • ドロップ可能なものを放棄し、独自の衝突検出をロールする: 独自の衝突検出を記述して、ドロップ可能な効果を模倣します。これにより、自由度が高まりますが、深刻なコーディングが必要になるため、カジュアルな要件には適していません。また、パフォーマンスの問題の影響を受けやすくなります。そうは言っても、あなたに有利に働く明らかなケースベースのショートカットがいくつかあるはずです.

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

于 2011-11-17T10:12:13.420 に答える
1

私はちょうどこの問題にぶつかりました。「ドラッグ」イベントでヒット テストを実装するだけであれば、それほど多くは必要ありません。ここでは、すべてのドロップ ターゲットを でタグ付けした.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');
    }
});
于 2012-05-14T14:13:22.123 に答える
0

これはかなり大雑把な (そしてコードのない) 解決策ですが、Droppable で hoverClass オプションを使用し、"hovering" という新しいクラスを作成して、Draggable が Droppable の上にあるときにのみ発生する Droppable の動作を設定することもできます。この「ホバリング」クラスは、(これは粗雑なビットです)何らかの無限ループまたはその他の種類のチェッカーを実行できます。これらのクラスを使用したことがないので、これ以上具体的なことは考えられません。=/

編集:さらに粗雑ですが、「ホバリング」クラスを使用して、Droppable の無効化と有効化を交互に行うことができます。ただし、これは間違いなく同期的に行い、時間の描写も十分に行います。disable と enable の呼び出し交互に行うと、イベントの 1 つがトリガーされますが、どちらを試して調べる必要があります。

于 2011-06-01T21:29:52.657 に答える