jQuery 可拖放和可滚动的 div

Posted

技术标签:

【中文标题】jQuery 可拖放和可滚动的 div【英文标题】:jQuery droppable and scrollable divs 【发布时间】:2011-06-21 22:27:39 【问题描述】:

jQuery UI 的可放置组件有一点问题,但我不太确定是因为我的代码还是组件中的错误。

我有一个固定宽度和高度的 div。该 div 的溢出-x 设置为隐藏,溢出-y 设置为自动。 在那个 div 中,我还有一些 div。如此之多以至于外部 div 开始滚动。每个内部 div 都是一个 droppable,接受一个在包装 div 之外的可拖动对象。

如果我将可拖动项目拖放到包装器中的某个位置,一切正常。问题是如果我将元素放在包装器 div 下方不久,甚至会触发 drop 事件。

我不太擅长解释问题;因此这里有一些重现问题的代码:

http://jsfiddle.net/2p56Y/

只需拖放“拖我!”带有滚动条的 div 下方的容器。没想到你会看到警报“dropped”。

现在一些有趣的事情:如果您向下滚动到项目“Test28”,然后将可拖动对象拖放到包装器下方,则不会触发放置事件。当您将某些东西放在隐藏元素上时,它们看起来仍然可以访问。

那么,这是一个错误还是我需要以不同的方式编写代码才能使其正常工作? (或两者兼而有之?:-))

【问题讨论】:

看起来一种可能的解决方法是在 drop 事件的函数中添加此条件: if ( $(this).position().top jsfiddle.net/5NuLa/2 @StefanS:对我来说似乎是一个合理的解决方案。 有点像,但我想知道是否有人想不出更优雅的东西。稍后我会研究 jquery-ui 可放置代码。 @StefanS 如果您在 Chrome 中检查 DOM 元素,您会更好地了解设置溢出属性时浏览器内部发生的情况。不幸的是,看起来事件仍然会触发,因为元素仍然被渲染和定位,但是被遮蔽了......即它对 DOM 是“可见的”。您将不得不编写代码来检查父容器边界的放置位置。我会试一试,看看结果如何。 @lthibodeaux 这就是我的想法。可惜没有过滤器可以真正告诉您元素是否可见。 :visible 在某些情况下可以完成这项工作,但在这种特定情况下则不行。也许可以扩展 droppable.js 的 intersect 功能来涵盖这种情况? github.com/jquery/jquery-ui/blob/master/ui/… 【参考方案1】:

根据父容器检查可放置元素的边界,如果可放置元素的底部高于父元素的顶部或可放置元素的顶部低于父元素的底部,则中断函数的执行:

$('.item').droppable( 
    activeClass: "ui-state-default",
    hoverClass: "ui-state-hover",
    accept: "#draggable",
    drop: function( event, ui ) 
        var cTop = $(this).closest(".box").position().top + parseInt($(this).closest(".box").css("margin-top"));
        var cBtm = $(this).closest(".box").position().top + parseInt($(this).closest(".box").css("margin-top")) + $(this).closest(".box").height();
        var dTop = $(this).position().top + parseInt($(this).css("margin-top"));
        var dBtm = $(this).position().top + parseInt($(this).css("margin-top")) + $(this).height()

        if (dBtm > cTop && dTop < cBtm) 
            alert("Dropped.");
        
    
);

示例:http://jsfiddle.net/lthibodeaux/2p56Y/6/

我意识到这并不优雅,但似乎可行。我承认只是粗略地测试了这个脚本。

【讨论】:

你应该让它更灵活一点,如果 droppable 在 DOM 中更下一层,它就会中断。我建议在相关元素中添加一个简单的.data,它引用适当的“父”元素,例如我们的例子中的$('.box') 谢谢,这似乎对我有用,而且比我的检查更通用:-) @davin 我会把它留给 OP。这是一个概念,而不是一个通用的解决方案。可以很容易地将 parent() 更改为最接近的(".box")。 编辑: 灵活。【参考方案2】:

你已经接受了一个答案,虽然我认为我应该把它放在那里:

可以通过设置$('.box, .item').droppable() 和默认情况下greedy:false 触发嵌套 div 的 drop 事件,然后触发更优雅的解决方法(基于事件冒泡,并且只真正处理一个级别的可见性,而不是递归)通过外部 div。

hasClass('box') 这样的简单元素检查意味着放置发生在有效区域中,因此您需要做的就是在内部放置事件缓存被放置的元素,然后在外部 div 的放置事件(如前所述,这种情况发生在片刻之后)随它去吧。

如果你在外部 div 之外放置,即使内部 div 放置事件会触发,外部 div 不会触发,所以除了无用的缓存事件之外什么都没有发生。唯一的问题是它看起来有一个非贪婪嵌套可放置的错误,jQuery 示例 http://jqueryui.com/demos/droppable/propagation.html 甚至对我来说都无法正常工作 - 它的行为就像它使用事件捕获而不是事件冒泡......

唯一的其他(诚然更合理的)解释是我误解了嵌套的 droppables 的行为方式。

【讨论】:

在我看来要聪明 50 倍。 嗯。这是一个好主意,但看起来浏览器仍然将溢出 div 视为其隐藏的内容仍然是可放置区域的一部分。我一直无法应用这个想法。 :[【参考方案3】:

最近解决了这个完全相同的问题,所以我想在这里分享一下。

我要提到的第一件事是解决了here 涉及嵌套可放置和事件冒泡的类似问题。下面的解决方案解决了非嵌套但重叠的 droppables 的问题。

问题(技术术语)

如果您使用他的说明查看 OP 的 jsfiddle,您会注意到这两个 DOM 元素彼此独立并且存在于相同的 DOM“级别”上。然而,正如 OP 和其他一些人所注意到的那样,jQuery UI 的 Droppable 似乎将 drop 事件冒泡到 DOM 元素,其中溢出的内容 underlapping 目标 droppable。

解决方案

当元素被拖放到目标 droppable 中时设置一个标志。检查底层 DOM 元素的 drop 事件中的标志。如果此标志包含指示它已被放置在目标可放置对象中的值,则返回 false、不执行任何操作或还原。无论您想做什么,都可以忽略 drop 事件。

对于我的特殊情况,我在目标 droppable 的 drop 事件中通过一个简单的属性值将我的标志设置为 ui.helper,如下所示:

$(ui.helper).attr('ignore-drop-event', "true");

在underlapping droppable 上的冒泡放置事件中,我检查该属性是否设置为true:

if ($(ui.helper).attr('ignore-drop-event') === "true") 
    // Ignore this drop event since it occurred as part of event bubbling

else 
    // This is a "real" drop event

这个解决方案依赖于假设

    事件冒泡顺序是一致的。 这意味着目标 droppable 将始终在underlapping droppable 元素之前接收事件。我不知道这是否总是正确的,但这个评估在我的测试中是准确的。

    属性值是在对冒泡丢弃事件进行标志检查之前定义的

希望这对其他人有所帮助。

【讨论】:

以上是关于jQuery 可拖放和可滚动的 div的主要内容,如果未能解决你的问题,请参考以下文章

可排序、可拖放和可拖动的容器

可拖动、可拖放和可移动的触摸幻灯片

jQuery 可拖动和可拖放,在可拖动 ul 上滚动

jquery可拖动和鼠标悬停

jquery拖放和克隆,找到元素的放置位置

拖放后向可拖放项目添加标签