将可拖动事件委托给父元素

3

我有可拖动的

  • 元素,它们嵌套在
      中,而后者又嵌套在< 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 不期望地闪烁。

      是否有一种方式可以委托事件,并仅允许处理 lidiv 祖先来处理事件?或者其他绕过此问题的方法?

      编辑:仍然很想知道是否有一种方法可以实现这一点,但现在我正在使用我在这里找到的解决方法。


  • 你把那些处理程序附加在哪里了? - Stephan Hoyer
    1个回答

    0

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

    尽量避免在附加的处理程序中从event.target/event.currentTarget直接操作DOM。

    有几个不同的地方:

    1. 你的ondragleaveondragenter处理程序应该只是在你的控制器/视图模型/存储中设置一些合适的“状态”属性。

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

    3. 你的“视图函数”再次运行。它然后反映出已更改的模型。你的动作不会改变视图,你的视图调用动作来影响模型,然后对这些变化做出反应。MVC,而不是MVVM


    模型

    在您的控制器中,设置一个模型来跟踪您需要展示拖放界面所需的所有状态。

    ctrl.dragging = m.prop(null)
    
    ctrl.groups = m.prop([
      {
        name: 'Group A',
        dragOver: false,
        items: ['Draggable One', 'Draggable Two']
      },
      ...
      // same structure for all groups
    ])
    

    视图

    在您的视图中,设置一个反映模型状态的用户界面。具有事件处理程序,可以传递有关操作的足够信息给控制器,以便它可以适当地响应操作并相应地操作模型。


    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)
      }
    }
    

    这是为了使可放置区域不在自己可拖动时改变颜色,这也是我认为你在答案中正在寻找的其中之一。

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接