JSFiddle - React, Tailwind, and code Playground
by tr00st
HTML
<div id="draggablesInsideMe">
<span class="draggableThing">Drag me!</span>
</div>
<hr />
<div id="targetsInsideMe">
<span class="target">Drop on me 1!</span>
<span class="target">Drop on me 2!</span>
<span class="target">Drop on me 3!</span>
<span class="target">Drop on me 4!</span>
<span class="target">Drop on me 5!</span>
<span class="target">Drop on me 6!</span>
<span class="target">Drop on me 7!</span>
<span class="target">Drop on me 8!</span>
<span class="target">Drop on me 9!</span>
<span class="target">Drop on me 10!</span>
<span class="target">Drop on me 11!</span>
<span class="target">Drop on me 12!</span>
</div>
Not here!
CSS
div#draggablesInsideMe {
width: 170px;
border: 1px black solid;
}
div#targetsInsideMe {
display: block;
width: 180px;
height: 125px;
border: 1px black solid;
overflow: auto;
}
span.draggableThing, span.target{
display: block;
border: 1px black solid;
background: gray;
width: 160px;
height: 50px;
}
JavaScript
// [email protected]
// MIT Licensed
$(".draggableThing").draggable({
snap: ".target.inView",
snapMode: "inner"
});
// TODO: Add a single-entry event queue to this - don't want more than one concurrent execution, but always want to have at an execution start after any event is fired. Shout if you want the reasoning behind this!
var updateVisibleTargets = function() {
var cont = $("#targetsInsideMe");
var height = cont.height();
var scrollPos = cont.scrollTop();
// Plan is - clear all from being in view, then find the first in view, then mark as active until we find one that's not in view - then drop out.
// Reset all
cont.find(".target").removeClass("inView");
cont.find(".target").each(function (i) {
var $this = $(this);
var topOffset = $this.offset().top - $this.height();
if (topOffset >= 0) {
console.debug(i+": "+topOffset+" -> "+height);
if (topOffset > height) {
return false;
}
$this.addClass("inView");
}
});
console.debug("------");
};
$("#targetsInsideMe").scroll(updateVisibleTargets);
updateVisibleTargets();