设置
我有一个可编辑的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);
将其放置在用户选择的位置。期望的功能
现在,我的用户可以放置一个花哨的图片 - 他们需要能够移动它。 用户需要能够单击并拖动图片(包括花式框)以将其放置在内容可编辑区域内任何位置。他们需要能够在段落之间移动它,甚至可以在段落内 - 在两个单词之间移动。
给我希望的是什么
请记住 - 在内容可编辑区域中,普通的
<img>
标签已经被用户代理赋予了这种可爱的拖放功能。默认情况下,您可以随意拖放<img>
标签;默认拖放操作的行为就像梦想一样。
考虑到这种默认行为在我们的标签上已经非常成功,我只想稍微扩展一下这种行为,包括少量更多的HTML,这似乎是很容易实现的。
我迄今为止的努力
首先,我使用可拖动属性设置了我的高级标签,并禁用了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');
});
这里是我的困境:这几乎完全可行。一切都运作良好,直到最后。在拖放事件中,我现在可以访问我试图插入到拖放位置的fancy box的HTML内容。现在我所需要做的就是将它插入正确的位置!问题是,我找不到正确的拖放位置,也没有任何方法可以将其插入其中。我一直希望能找到一些类似于“dropLocation”对象的东西来转储我的fancy box,例如dropEvent.dropLocation.content=myFancyBoxHTML;,或者至少有一些拖放位置值,以便自己找到将内容放置在那里的方法?我有什么吗?我是完全错误的吗?我完全错过了什么吗?我尝试使用document.execCommand('insertHTML',false,content);像我预期的那样,但不幸的是,选择插入符号并没有位于我希望的精确拖放位置。
我发现如果注释掉所有的
event.preventDefault();
,选择符号就会变得可见。当用户准备放置时,将拖动悬停在contenteditable上,小的选择符号可以看到,随着用户光标和拖放操作之间的字符移动,指示选择符号表示精确的放置位置。我需要这个选择符号的位置。
通过一些实验,我尝试在拖放事件和拖放结束事件中使用execCommand-insertHTML'ing,但都没有在放置选择符号的位置插入HTML,而是使用了拖动操作之前选择的任意位置。因为选择符号在拖动过程中是可见的,所以我想出了一个计划。
有一段时间,我试图在拖动事件中插入一个临时标记,比如
<span class="selection-marker">|</span>
,紧接着$('.selection-marker').remove();
,试图让浏览器不断(在拖动过程中)删除所有选择标记,并在插入点添加一个标记——从本质上讲,无论何时都在插入点留下一个标记。当然,我的计划是用我拥有的拖动内容替换这个临时标记。
当然,所有这些都没有起作用:我无法像计划的那样在显然可见的选取符插入选择标记——再次说明,execCommand-insertedHTML会放置在拖动操作之前的选择符位置。
哎呀,那我错过了什么? 该怎么做呢?
如何获取或插入拖放操作的精确位置?我觉得这显然是拖放操作中常见的操作之一——肯定有我忽略的重要且明显的细节吧?我是否需要深入JavaScript,或者也许有一种方法可以仅使用属性(例如draggable、droppable、contenteditable)和一些花里胡哨的CSS3来完成这项操作?
我还在寻找——还在尝试——一旦我发现自己失败的原因,我就会回复 :)
寻找继续(原帖后的编辑)
Farrukh提出了一个好建议--使用:
console.log( window.getSelection().getRangeAt(0) );
为了确定光标的实际位置,我将以下代码放入dragover事件中,因为在这个事件中,我认为选择光标在contenteditable中的可编辑内容之间可见地跳动。
然而,返回的Range对象报告的偏移索引属于拖放操作之前的选择光标。
这是一次英勇的尝试。谢谢Farrukh。
那么这里发生了什么?我有一种感觉,我看到的小光标不是选择光标!我觉得它是一个冒名顶替者!
进一步检查!
事实证明,它确实是一个冒牌货!在整个拖动操作期间,真正的选择光标保持不变!你可以看到这个小家伙!
自然地,在 dragover 事件中,您可能需要移动插入标记。您可以使用事件的 clientX 和 clientY 属性来确定鼠标指针的位置,就像其他鼠标事件一样。哎呀,这是否意味着我应该根据 clientX 和 clientY 自己找出插入符号的位置?使用鼠标坐标自己确定选择符号的位置?太可怕了!除非我或者在这里阅读此内容的其他人能找到一个明智的解决方案,否则我将在明天研究一下。 :)
window.getSelection().getRangeAt(0);
进行了 console.log,它返回的 Range 对象显示了 拖放操作之前选择插入符号最初所在的索引值。不过还是谢谢你,这是个好想法。 - ChaseMoskal