【发布时间】:2013-01-18 15:55:08
【问题描述】:
设置
所以,我有一个内容可编辑的 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——这似乎应该很容易实现.
到目前为止我的努力
首先,我用可拖动属性设置了我喜欢的<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 所在的位置,而是使用在拖动操作之前选择的任何位置.
因为选择插入符号在拖动过程中是可见的,所以我制定了一个计划。
有一段时间,我试图在拖动事件中插入一个临时标记,例如 <span class="selection-marker">|</span>,就在 $('.selection-marker').remove(); 之后,试图让浏览器不断(在拖动期间)删除所有选择标记和然后在插入点添加一个 - 本质上是在任何时候在插入点所在的任何位置留下一个标记。当然,计划是用我拥有的拖动内容替换这个临时标记。
当然,这些都不起作用:我无法按计划将选择标记插入到明显可见的选择插入符处——同样,execCommand-insertedHTML 在拖动之前将自身放置在选择插入符所在的任何位置操作。
呵呵。那么我错过了什么?它是怎么做的?
如何获取或插入拖放操作的精确位置? 我觉得这显然是拖放操作中的常见操作——我肯定忽略了某种重要且明显的细节吗?我什至必须深入了解 JavaScript,或者也许有一种方法可以只使用可拖动、可放置、可内容编辑和一些花哨的 CSS3 等属性?
我仍在寻找——仍在修补——我会在发现我一直失败的地方后立即回复:)
狩猎继续(原帖后编辑)
Farrukh 提出了一个很好的建议——使用:
console.log( window.getSelection().getRangeAt(0) );
查看选择插入符号的实际位置。我将它放入 dragover 事件中,此时我发现选择插入符号明显在 contenteditable 中的可编辑内容之间跳跃。
唉,返回的 Range 对象在拖放操作之前报告属于选择插入符号的偏移索引。
这是一次勇敢的努力。谢谢法鲁克。
那么这里发生了什么?我感觉我看到的那个小选择插入符号在跳来跳去,根本就不是选择插入符号!我认为这是一个冒名顶替者!
经进一步检查!
原来,这是一个冒名顶替者! 真正的选择插入符号在整个拖动操作期间保持原位!你可以看到那个小虫子!
我在阅读MDN Drag and Drop Docs,发现了这个:
当然,您可能还需要在拖动事件周围移动插入标记。 您可以像使用其他鼠标事件一样使用事件的 clientX 和 clientY 属性来确定鼠标指针的位置。
哎呀,这是否意味着我应该根据 clientX 和 clientY 自己解决这个问题?自己使用鼠标坐标确定选择插入符号的位置?吓人!!
我明天会考虑这样做 -- 除非我自己或其他阅读本文的人能找到一个理智的解决方案 :)
【问题讨论】:
-
+1 问题问得好!
-
当图像在该点下降时,选择插入符号就是您要插入的位置。不是吗?如果是则使用此stackoverflow.com/questions/1891444/…
-
@FarrukhSubhani:看起来很有希望。我在 dragover 期间 console.log'd
window.getSelection().getRangeAt(0);(这是选择插入符号可见的位置),它返回的 Range 对象显示 选择插入符号最初所在的索引值,在拖放操作之前。 谢谢,这是个好主意。
标签: javascript html css drag-and-drop draggable