开发者问题收集

HTML5 拖放占位符将 parentNode 设置为 null onLeave (js vanilla)

2017-07-14
1146

我正在尝试为自己的网站创建一个编辑器(基于 contentEditables 和 HTML5 拖放功能)。

这是 fiddle: https://jsfiddle.net/5bw320zo/1/ 请注意,代码很乱,它只是一个 POC


问题:

每次我 悬停 占位符 时,占位符的 parentNode 似乎都设置为 null。

Uncaught TypeError: Cannot read property 'removeChild' of null at _detach at HTMLDivElement._handleDragLeave


代码说明:

我在初始化时创建了一个简单的 div 元素:

placeholder = d.createElement('div');
placeholder.setAttribute('style', 'height: 25px; background-color: red;');

每个容器都有自己的事件:

_on(elements, 'dragenter', _handleDragEnter);
_on(elements, 'dragover', _handleDragOver);
_on(elements, 'dragleave', _handleDragLeave);
_on(elements, 'drop', _handleDrop);

悬停时,我检查它是否有子元素,如果没有,我只需附加元素,否则我会在前面或后面添加元素(取决于鼠标高度的位置):

function _handleDragOver(e) {
  var targetBoundingBox,
    targetMouseY;

  targetBoundingBox = e.target.getBoundingClientRect();
  targetMouseY = e.pageY - targetBoundingBox.top;

  if (e.target.hasChildNodes()) {
    // TODO check Y axis (/2)
  } else {
    c.log('within');
    _within(e.target, placeholder);
  }
}

当元素鼠标离开时,我将占位符(全局变量)从其自己的父级中分离出来:

function _handleDragLeave(e) {
  placeholder.parentNode.removeChild(placeholder);
}

这真是让我烦透了。当检查离开时的源代码时,占位符元素仍然在正确的位置。有什么想法吗?

仅供参考,如果你们对集成占位符有更好的想法,请告诉我们(没有 jquery 或现有库)。

2个回答

这里有几个问题...

首先,当鼠标悬停在占位符上时,您的 _handleDragOver 将始终执行其 else 条件,因为它没有子项。

function _handleDragOver(e) {
    var targetBoundingBox,
      targetMouseY;

    targetBoundingBox = e.target.getBoundingClientRect();
    targetMouseY = e.pageY - targetBoundingBox.top;

    if (e.target.hasChildNodes()) {
      // TODO check Y axis (/2)
    } else {
      c.log('within');
      _within(e.target, placeholder); //<-- this guy
    }
  }

dragOver 在鼠标悬停在有效的放置目标上时不断触发。

接下来, _within 函数:

  function _within(target, element) {
    target.appendChild(element);
  }

如果占位符尚未被您的 _detach 函数删除,这个小家伙会给您带来麻烦。如果您要将其拖放到占位符上,而它尚未被移除,您将尝试将其附加到自身……这将导致无趣的烟花。

现在对于 _detach

  function _detach(element) {
    element.parentNode.removeChild(element);
  }

这将在您的 _handleDragLeave 函数中调用。当您放下拖拽物时,将触发 dragLeave 事件以移除占位符。

错误就在这里……将鼠标悬停在占位符上会导致其被 dragLeave 分离,但随后又被 dragOver 附加回来,如此反复,导致闪烁。您收到的错误是由尝试移除占位符的 drop 事件引起的,此时它已被 dragLeave 移除,但尚未被 dragOver 重新附加,然后它又尝试再次因拖拽而移除它。

Kyle Richardson
2017-07-20

我以前做过一些拖放操作。当时我的问题是,我没有意识到在进行拖放操作时需要使用 DataTransfer 对象。

DataTransfer 对象

JS fiddle 示例

步骤 1:拖动处理程序:

function _handleDragStart(e) {
  e.dataTransfer.setData('myData', 'somedata');
  e.dataTransfer.effectAllowed = 'move';
  //no e.preventDefault() here or it will not work.
}

步骤 2:拖动处理程序:

function _handleDragOver(e) {
  var data = e.dataTransfer.getData('myData');
  if(data && //is this valid drop data){
    e.dataTransfer.dropEffect = 'move';
    e.preventDefault();
  }
}

步骤 3:放置处理程序:

function _handleDrop(e) {
  var data = e.dataTransfer.getData('myData');
  if(data && //is this valid drop data){
    //do something with your dropped data.
    //re-render component inside editor-container
    e.preventDefault();
  }
}

步骤 4:拖动结束处理程序:

function _handleDragEnd(e) {
  e.dataTransfer.clearData();
  //Check if drop was completed and do cleanup.
  //remove component from editor-item-list
}

e.dataTransfer.dropEffect 用于告知拖放 API 可以执行放置操作。没有它, drop 将永远不会触发。它还用于更改光标的图标。

e.dataTransfer.setData 仅将数据作为字符串。这可以通过将其转换为 JSON 来解决,或者您可以在编辑器中自己处理数据传输。

我发现在进行拖放时使用数据比使用 DOM 元素更简单。传输数据并在放置操作后重新呈现它。

更新 1

在 chrome 59 中, e.dataTransfer.getDatadrag overdrag end 不起作用 。也许您最好自己进行数据传输。请记住,您仍然需要设置 dropEffect

Lars-Kristian Johansen
2017-07-21