jQuery droppable ( jQuery 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; }
あなたが言ったように、over
(対応するout
のように)はドロップ可能オブジェクトで一度だけ発生します。一方、draggableのdrag
イベントは、マウスが動くたびに発生し、タスクに適しているようです。ただし、この戦略には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");
}
});
私は同じ問題を抱えており、この比較的まれなニーズを見つけた他の人に指示を与える場合に備えて共有する2つのソリューションについて考えています。
2つのdivソリューション:行の各セルに2つのdivを追加します。UIの干渉から保護するために、スタックされるように配置され、高さと幅がそれぞれ50%で、zインデックスが-1に設定されています。次に、これらのドロップ可能オブジェクトを作成し、それらの「over」および「out」イベントを使用して、親セルまたは行のクラスを切り替えます。
ドロップ可能を破棄し、独自の衝突検出をロールする:ドロップ可能効果を模倣する独自の衝突検出を記述します。これはより多くの自由を与えますが、いくつかの深刻なコーディングにつながるため、カジュアルな要件ではありません。また、パフォーマンスの問題の影響を受けやすくなります。とはいえ、あなたの好みで機能する明らかなケースベースのショートカットがいくつかあるはずです。
ローコードソリューションへの他のアプローチについて聞いてみたいと思います。
別の方法は、クラスまたは他の選挙人をヒント要素に追加することです。次に、ドラッグ可能な定義のドラッグハンドラーで、ヒントの位置を更新します。
$('#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);
}
});
私はこの問題にぶつかった。 「ドラッグ」イベントでヒットテストを実装するだけの場合、それほど多くは必要ありません。ここでは、すべてのドロップターゲットに.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');
}
});
これはかなり大雑把な(そしてコードレスな)ソリューションですが、DroppableでhoverClassオプションを使用し、「hovering」という新しいクラスを作成して、DraggableがDroppableの上にホバーしているときにのみ発生するDroppable動作を設定できます。この「ホバリング」クラスは、(これは粗雑なビットです)ある種のエンドレスループまたは他の種類のチェッカーを実行できます。私はこれらのクラスを以前に使用したことがないので、この時点を過ぎてこれ以上の詳細を考えることはできません。 = /
編集:さらに大まかに言えば、「ホバリング」クラスを使用して、Droppableを無効化および有効化することもできます。私は間違いなくを同期的に行いますが、寛大な時間の描写も行います。 disableとenableを交互に呼び出すすべきイベントの1つをトリガーしますが、それを調べるには実験する必要があります。