HTML5 拖放占位符将 parentNode 设置为 null onLeave (js vanilla)
我正在尝试为自己的网站创建一个编辑器(基于 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 或现有库)。
这里有几个问题...
首先,当鼠标悬停在占位符上时,您的
_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 重新附加,然后它又尝试再次因拖拽而移除它。
我以前做过一些拖放操作。当时我的问题是,我没有意识到在进行拖放操作时需要使用 DataTransfer 对象。
步骤 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.getData
在
drag over
和
drag end
中
不起作用
。也许您最好自己进行数据传输。请记住,您仍然需要设置
dropEffect
。