将可拖动事件委托给父元素
Posted
技术标签:
【中文标题】将可拖动事件委托给父元素【英文标题】:Delegating Draggable Events to Parent Elements 【发布时间】:2015-11-02 05:55:14 【问题描述】:我将可拖动的 li
元素嵌套在 ul
中,而这些元素又嵌套在 div
中,如下所示:
<div class='group'>
<div class='header'>
// some stuff here
</div>
<ul>
<li draggable='true'>
Stuff I want to drag and drop to another div.group
</li>
</ul>
</div>
这些div
元素有多个,我正在尝试实现拖放功能,将一个div
组的li
元素移动到另一个组。
我已经在此处连接了 ondragenter
、ondragleave
回调:
// controller using mithril.js
ctrl.onDragLeave = function ()
return function (event)
var target;
// Using isDropzone to recursively search for the appropriate div.group
// parent element, as event.target is always the children inside it
if ((target = isDropzone(event.target)) != null)
target.style.background = "";
;
ctrl.onDragEnter = function ()
return function (event)
var target;
if ((target = isDropzone(event.target)) != null)
target.style.background = "purple";
;
;
function isDropzone(elem)
if(elem == null)
return null;
return elem.className == 'group' ? elem: isDropzone(elem.parentNode)
当回调的event.target
总是div
内的嵌套子元素时,问题就出现了,例如li
,因此回调会不断被触发。在这种情况下,我将通过回调更改 div.group
的颜色,导致 div.group
不受欢迎地闪烁。
有没有办法委派事件并只允许li
的祖父div
处理事件?或任何其他解决此问题的方法?
编辑:仍然很想知道是否有办法做到这一点,但现在我正在使用我找到的解决方法here。
【问题讨论】:
你把这些处理程序也附加到了哪里? 【参考方案1】:所以这将适合“你需要从不同的角度解决这个问题”的答案类别。
避免 - 尽可能 - 在附加的处理程序中从 event.target/event.currentTarget 操作 DOM。
一些不同的事情:
您的 ondragleave
和 ondragenter
处理程序应该在您的控制器/viewModel/stores 中简单地设置一些适当的“状态”属性
当处理程序被解析时,这通常会触发 Mithril 中的重绘。在内部m.startComputation()
启动,你的处理程序被调用,然后m.endComputation()
您的“查看功能”再次运行。然后它反映了更改后的模型。您的操作不会更改视图,您的视图会调用影响模型的操作,然后对这些更改做出反应。 MVC,而不是 MVVM
型号
在您的控制器中,设置一个模型来跟踪您显示拖放 UI 所需的所有状态
ctrl.dragging = m.prop(null)
ctrl.groups = m.prop([
name: 'Group A',
dragOver: false,
items: ['Draggable One', 'Draggable Two']
,
...
// same structure for all groups
])
查看
在您看来,设置一个反映您的模型状态的 UI。拥有事件处理程序,将有关操作的足够信息传递给控制器 - 足以让控制器正确响应操作并相应地操作模型
return ctrl.groups.map(function (group, groupIdx)
return m('.group',[
m('.header', group.name),
m('ul',
style: background: (group.dragOver ? 'blue' : ''),
ondragleave: function () ctrl.handleDragLeave(groupIdx),
ondragenter: function () ctrl.handleDragEnter(groupIdx),
ondrop: function () ctrl.handleDrop(groupIdx),
ondragover: function (e) e.preventDefault()
,
group.items.map(function (item, itemIdx)
return m('li',
draggable: true,
ondragstart: function () ctrl.handleDragStart(itemIdx, groupIdx)
,
item
)
)
])
)
现在已设置好,组可以通过对控制器中的状态/模型更改做出反应来正确显示。我们不需要操纵 dom 来说一个组有一个新项目,一个组需要一个新的背景颜色,或者任何东西。我们只需要附加事件处理程序,以便控制器可以操作您的模型,然后视图将根据该模型重绘。
控制器
因此,您的控制器可以拥有包含更新模型所需操作的所有信息的处理程序。
您的控制器上的一些处理程序如下所示:
ctrl.handleDragStart = function (itemIdx, groupIdx)
ctrl.dragging(itemIdx: itemIdx, groupIdx: groupIdx)
ctrl.handleDragEnter = function (groupIdx)
ctrl.groups()[groupIdx].dragOver = true
ctrl.handleDragLeave = function (groupIdx)
ctrl.groups()[groupIdx].dragOver = false
ctrl.handleDrop = function (toGroupIdx)
var groupIdx = ctrl.dragging().groupIdx
var itemIdx = ctrl.dragging().itemIdx
var dropped = ctrl.groups()[groupIdx].items.splice(itemIdx, 1)[0]
ctrl.groups()[toGroupIdx].items.push(dropped)
ctrl.groups()[toGroupIdx].dragOver = false
ctrl.dragging(null)
尝试坚持 Mithril 的 MVC 模型 事件处理程序在您的控制器上调用操作,该控制器操作模型。然后视图会对这些模型中的变化做出反应。这绕过了与 DOM 事件的细节纠缠在一起的需要。
这是一个完整的 JSbin 示例,展示了您要达到的目标:
https://jsbin.com/pabehuj/edit?js,console,output
我得到了想要的效果,完全不用担心事件委托。
另外,请注意在 JSbin 中,ondragenter
处理程序:
ondragenter: function ()
if (ctrl.dragging().groupIdx !== groupIdx)
ctrl.handleDragEnter(groupIdx)
这样可放置区域不会在其自身的可拖动区域中改变颜色,这是我认为您在答案中寻找的东西之一。
【讨论】:
以上是关于将可拖动事件委托给父元素的主要内容,如果未能解决你的问题,请参考以下文章