【问题标题】: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 元素移动到另一个组。

我已经在此处连接了 ondragenterondragleave 回调:

 // 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

【问题讨论】:

  • 你把这些处理程序也附加到了哪里?

标签: javascript draggable mithril.js


【解决方案1】:

所以这将适合“你需要从不同的角度解决这个问题”的答案类别。

避免 - 尽可能 - 在附加的处理程序中从 event.target/event.currentTarget 操作 DOM。

一些不同的事情:

  1. 您的 ondragleaveondragenter 处理程序应该在您的控制器/viewModel/stores 中简单地设置一些适当的“状态”属性

  2. 当处理程序被解析时,这通常会触发 Mithril 中的重绘。在内部m.startComputation() 启动,你的处理程序被调用,然后m.endComputation()

  3. 您的“查看功能”再次运行。然后它反映了更改后的模型。您的操作不会更改视图,您的视图会调用影响模型的操作,然后对这些更改做出反应。 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)
  }
}

这样可放置区域不会在其自身的可拖动区域中改变颜色,这是我认为您在答案中寻找的东西之一。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-27
    • 2018-03-13
    相关资源
    最近更新 更多