【问题标题】:Only drop to element that is seen仅下降到可见的元素
【发布时间】:2012-08-23 15:31:40
【问题描述】:

我有以下几点:

我正在尝试设置它,以便当您拖动该项目时,它只会被拖放到您可以看到的 div 元素上,并且不会被 覆盖.

所以我用了这个js:

$(".draggable").draggable({
    helper: "clone"
})
$("#bottom, .draggable").droppable({
    drop: function(event, ui) {
        var $this = $(this),
            $dragged = $(ui.draggable);
        $this.append($dragged.clone());
    },
    hoverClass: "dragHover"
})​

但它会将元素放在两个位置,即使只有一个放置区域可见!

我该如何解决它以使这种情况不会发生?

小提琴:http://jsfiddle.net/maniator/Wp4LU/


额外的信息来重新创建页面没有小提琴:

HTML:

  <div id="top">
    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>

    <div class="draggable">
      Lorem ipsum dolor sit amet
    </div>
  </div>

  <div id="bottom"></div>

CSS:

.draggable {
    border: 1px solid green;
    background: white;
    padding: 5px;
}

.dragHover{
    background: blue;
}

#top {
    height: 500px;
    overflow-y: scroll;
}
#bottom {
    height: 150px;
    overflow-y: scroll;
    border: red solid 4px;
}

【问题讨论】:

  • 由于它没有解决实时服务器中的问题,这里有一个 fiddle 使用 disabled 属性来模仿 event.stopImmediatePropagation 到其他 UI 小部件,希望如果没有更好的方法可以改进找到了。
  • @Neal 你不认为我们可以使用一些像 $this.is(":visible") 这样的 jquery 属性。我对jquery了解不多,但你可以说这是一个新手的尝试

标签: javascript jquery jquery-ui drag-and-drop


【解决方案1】:

尝试使用accept 功能进行设置。 The working demo.

$("#bottom, .draggable").droppable({
    drop: function(event, ui) {
        var $this = $(this),
            $dragged = $(ui.draggable);
        $this.append($dragged.clone());
    },
    accept: function () {
        var $this = $(this), divTop= $("#top");
        if ($this.is(".draggable")) {
          return $this.offset().top < divTop.offset().top + divTop.height() ;
        }
        return true;
    },
    hoverClass: "dragHover"
})​;​

【讨论】:

  • 太棒了^_^这似乎工作:-)。我得再测试一下:-D。非常感谢!
【解决方案2】:

如果我没听错 - 这个应该可以解决你的问题 - http://jsfiddle.net/Wp4LU/60/ 你也可以编写自定义接受函数 - http://jqueryui.com/demos/droppable/#option-accept

代码:

var draggableList = $('#top');

$(".draggable").draggable({
    helper: "clone"
});
$("#bottom, .draggable").droppable({
    drop: function(event, ui) {
        var $this = $(this),
            $dragged = $(ui.draggable);

        if ($this.hasClass("draggable")) {
            if ($this.position().top >= draggableList.height() || 
                $this.position().top + $this.outerHeight() >= 
                draggableList.height()) 
                return;
        }

        $this.append($dragged.clone());
    },
    hoverClass: "dragHover"
})​;​

【讨论】:

  • 这在小提琴中效果很好......但它似乎不适用于我的真实代码(看起来略有不同)......
【解决方案3】:

根据来源 (jquery.ui.droppable.js),drop 操作将搜索每个符合条件的 droppable 并将 drop 函数应用于与其相交的每个:

drop: function(draggable, event) {

    var dropped = false;
    $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {

        if(!this.options) return;
        if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance))
            dropped = this._drop.call(this, event) || dropped;

(旧版本的最后一个“OR”条件颠倒了,所以它只适用于单个 droppable。使用 jQuery 1.5.2 / jQuery UI 1.8.9 试试你的小提琴,看看它只下降到一个元素,虽然是“错误的”...)

目前在$.ui.intersect 函数中实现的每种容差模式仅考虑 (x,y) 坐标:

switch (toleranceMode) {
    case 'fit':
        return (l <= x1 && x2 <= r
            && t <= y1 && y2 <= b);
        break;
    case 'intersect':
        return (l < x1 + (draggable.helperProportions.width / 2) // Right Half
            && x2 - (draggable.helperProportions.width / 2) < r // Left Half
            && t < y1 + (draggable.helperProportions.height / 2) // Bottom Half
            && y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
        break;
    ...

因此,除非有人添加了 z-index 感知容差模式,否则您唯一的选择就是以某种方式解决该问题。我建议首先将每个可放置的候选者添加到一个集合中,当该放置时,只选择离屏幕“最近”的那个:

$("#bottom, .draggable").droppable({
    over: function(event, ui) {
        if ( !ui.draggable.data("drop-candidates") )
            ui.draggable.data("drop-candidates",[]);
        ui.draggable.data("drop-candidates").push(this);
    },
    out: function(event, ui) {
        var that = this,
            candidates = ui.draggable.data("drop-candidates") || [];
        ui.draggable.data("drop-candidates", $.grep(candidates, function(e) {
            return e != that;
        });
    },
    drop: function(event, ui) {
        var $this = $(this),
            $dragged = $(ui.draggable);
        var candidates = $.data("drop-candidates").sort(closestToScreen);
        if ( candidates[0] == this )
            $this.append($dragged.clone());
    },
    hoverClass: "dragHover"
})​

现在,实现closestToScreen 比较器是棘手的部分。 W3C CSS Specification 描述了渲染引擎应该如何对要绘制的元素进行排序,但到目前为止我还没有找到一种简单的方法来访问这些信息。我也在SOasked this question这里,也许有人会找到一个好方法。


附:如果修改 jQuery UI 源 一个选项,您可以尝试使用 document.getElementFromPoint 实现 z-index 感知容差模式,如 this answer 建议的问题:

var x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
    y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height;
switch (toleranceMode) {
    ...
    case 'z-index-aware':
        return document.elementFromPoint(x1,y1) == droppable;
        break;

(这将确保将可拖动元素左上角正下方的元素视为“足够好”作为放置目标 - 不理想,但比我们目前所拥有的要好; 类似的解决方案可以改为使用鼠标指针坐标)

而且,不,您不能将这种方法与之前介绍的解决方法一起使用:在拖放发生的那一刻,drag helper 是最靠近屏幕的元素...(编辑: d'oh!如果实现为容差模式,它也不会工作,出于同样的原因......)

【讨论】:

    【解决方案4】:

    如果你只是想放在元素上,你可以看到你可以改变你的选择器:

    $(".draggable:visible").draggable({
        helper: "clone"
    });
    $("#bottom, .draggable:visible").droppable({
        drop: function(event, ui) {
            var $this = $(this),
                $dragged = $(ui.draggable);
            $this.append($dragged.clone());
        },
        hoverClass: "dragHover"
    })​;
    

    或者当你隐藏一个元素时,用别的东西改变它的可拖动类。

    【讨论】:

    • @Neal 看来我当时没有得到真正的问题。看起来你从另一个帖子中得到了答案。祝你好运
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-01
    • 1970-01-01
    • 2022-11-25
    • 2012-12-15
    • 1970-01-01
    相关资源
    最近更新 更多