设置
所以,我有一个内容可编辑的 div —— 我正在制作一个所见即所得的编辑器:粗体、斜体、格式等,以及最近的:插入精美的图像(在精美的框中,带有标题)。
<a class="fancy" href="i.jpg" target="_blank">
<img alt="" src="i.jpg" />
Optional Caption goes Here!
</a>
用户通过我向他们展示的对话框添加这些精美的图像:他们填写详细信息,上传图像,然后就像其他编辑器功能一样,我用来document.execCommand('insertHTML',false,fancy_image_html);
在用户选择时将其放入。
所需的功能
所以,现在我的用户可以在一个精美的图像中扑通一声——他们需要能够移动它。用户需要能够单击并拖动图像(花哨的框和所有)以将其放置在 contenteditable 中他们喜欢的任何位置。他们需要能够在段落之间移动它,甚至在段落内——如果他们愿意的话,可以在两个词之间移动。
是什么给了我希望
请记住——在内容可编辑中,普通的旧<img>
标签已经被用户代理所祝福,具有这种可爱的拖放功能。默认情况下,您可以随意拖放<img>
标签;默认的拖放操作的行为就像人们梦寐以求的那样。
因此,考虑到这种默认行为如何在我们的<img>
伙伴中如此出色地发挥作用——我只想稍微扩展这种行为以包含更多的 HTML——这似乎应该很容易实现。
我迄今为止的努力
首先,我使用 draggable 属性设置了我的花哨<a>
标签,并禁用了 contenteditable (不确定这是否有必要,但它似乎也可以关闭):
<a class="fancy" [...] draggable="true" contenteditable="false">
然后,因为用户仍然可以将图像拖出花哨的<a>
盒子,我不得不做一些 CSS。我在 Chrome 中工作,所以我只向您展示 -webkit- 前缀,尽管我也使用了其他前缀。
.fancy {
-webkit-user-select:none;
-webkit-user-drag:element; }
.fancy>img {
-webkit-user-drag:none; }
现在用户可以拖动整个花哨的盒子,部分褪色的点击拖动表示图像反映了这一点——我可以看到我现在正在拿起整个盒子:)
我尝试了几种不同 CSS 属性的组合,上面的组合对我来说似乎很有意义,而且效果最好。
我希望仅此 CSS 就足以让浏览器将整个元素用作可拖动项目,自动授予用户我梦寐以求的功能......然而,它似乎比这更复杂。
HTML5 的 JavaScript 拖放 API
这种拖放的东西似乎比它需要的更复杂。
所以,我开始深入研究 DnD api 文档,现在我陷入了困境。所以,这就是我所设计的(是的,jQuery):
$('.fancy')
.bind('dragstart',function(event){
//console.log('dragstart');
var dt=event.originalEvent.dataTransfer;
dt.effectAllowed = 'all';
dt.setData('text/html',event.target.outerHTML);
});
$('.myContentEditable')
.bind('dragenter',function(event){
//console.log('dragenter');
event.preventDefault();
})
.bind('dragleave',function(event){
//console.log('dragleave');
})
.bind('dragover',function(event){
//console.log('dragover');
event.preventDefault();
})
.bind('drop',function(event){
//console.log('drop');
var dt = event.originalEvent.dataTransfer;
var content = dt.getData('text/html');
document.execCommand('insertHTML',false,content);
event.preventDefault();
})
.bind('dragend',function(event){
//console.log('dragend');
});
所以这就是我卡住的地方:这几乎完全有效。几乎完全。我有一切工作,直到最后。在放置事件中,我现在可以访问我试图在放置位置插入的精美盒子的 HTML 内容。我现在需要做的就是将它插入正确的位置!
问题是我找不到正确的放置位置,或任何插入它的方法。我一直希望找到某种“dropLocation”对象来将我的花哨的盒子倾倒到dropEvent.dropLocation.content=myFancyBoxHTML;
其中,或者至少是某种放置位置值,以便找到我自己的方式将内容放在那里?
我得到了什么吗?
我做错了吗?我完全错过了什么吗?
我尝试document.execCommand('insertHTML',false,content);
像我预期的那样使用,但不幸的是,我在这里失败了,因为选择插入符号没有像我希望的那样位于精确的放置位置。
我发现如果我注释掉所有的event.preventDefault();
,选择插入符号变得可见,并且正如人们希望的那样,当用户准备放下时,将他们的拖动悬停在 contenteditable 上,可以看到小选择插入符号在字符之间运行跟随用户的光标和放置操作——向用户指示选择插入符号代表精确的放置位置。我需要这个选择插入符号的位置。
通过一些实验,我在 drop 事件和 dragend 事件期间尝试了 execCommand-insertHTML'ing - 既不将 HTML 插入到 drop-selection-caret 所在的位置,而是使用在拖动操作之前选择的任何位置。
因为选择插入符号在拖动期间可见,所以我制定了一个计划。
有一段时间,我试图在 dragover 事件中插入一个临时标记,例如<span class="selection-marker">|</span>
,就在 之后$('.selection-marker').remove();
,试图让浏览器不断(在 dragover 期间)删除所有选择标记,然后在插入点添加一个 -基本上在任何时候在插入点所在的地方留下一个标记。当然,计划是用我拥有的拖动内容替换这个临时标记。
当然,这些都不起作用:我无法按计划将选择标记插入到明显可见的选择插入符号处——再次,在拖动操作之前,execCommand-insertedHTML 将自身放置在选择插入符号所在的任何位置。
呵呵。那么我错过了什么?它是如何完成的?
如何获取或插入拖放操作的精确位置? 我觉得这显然是拖放操作中的常见操作——我肯定忽略了某种重要而明显的细节吗?我什至必须深入研究 JavaScript,或者也许有一种方法可以仅使用可拖动、可放置、可内容编辑和一些花哨的 CSS3 等属性来做到这一点?
我仍在寻找——仍在修补——我会在发现我一直失败的地方后立即回复:)
狩猎继续(原帖后编辑)
Farrukh 提出了一个很好的建议——使用:
console.log( window.getSelection().getRangeAt(0) );
查看选择插入符号的实际位置。我把它放到了dragover事件中,当我发现选择插入符号在contenteditable中我的可编辑内容之间明显跳跃时。
唉,返回的 Range 对象在拖放操作之前报告属于选择插入符号的偏移索引。
这是一次英勇的努力。谢谢法鲁克。
那么这里发生了什么?我感觉到我看到的小选择插入符号根本不是选择插入符号!我觉得是骗子!
经进一步检查!
原来,这是一个冒名顶替者!在整个拖动操作期间,真正的选择插入符号保持不变!你可以看到小虫子!
我正在阅读MDN Drag and Drop Docs,发现了这个:
当然,您可能还需要在拖动事件周围移动插入标记。您可以将事件的 clientX 和 clientY 属性与其他鼠标事件一起使用来确定鼠标指针的位置。
哎呀,这是否意味着我应该根据clientX和clientY自己弄清楚?自己使用鼠标坐标确定选择插入符号的位置?可怕的!!
明天我会考虑这样做——除非我自己,或者这里的其他人读到这个,可以找到一个理智的解决方案:)