Cha*_*kal 60 javascript html5 drag-and-drop draggable css3
所以,我有一个令人满意的div - 我正在制作一个WYSIWYG编辑器:粗体,斜体,格式化,无论如何,最近:插入花哨的图像(在一个花哨的盒子里,带有标题).
<a class="fancy" href="i.jpg" target="_blank">
<img alt="" src="i.jpg" />
Optional Caption goes Here!
</a>
Run Code Online (Sandbox Code Playgroud)
用户使用我给他们提供的对话框添加这些奇特的图像:他们填写详细信息,上传图像,然后很像其他编辑器功能,我用document.execCommand('insertHTML',false,fancy_image_html);它来根据用户的选择进行填充.
所以,现在我的用户可以拍摄一个奇特的图像 - 他们需要能够移动它.用户需要能够单击并拖动图像(花式框和所有图像),以便将其放置在满足其中的任何位置.他们需要能够在段落之间移动,甚至在段落之间 - 如果他们想要的话,在两个单词之间移动它.
请记住 - 在一个可信的,简单的旧<img>标签已经被用户代理祝福,具有这种可爱的拖放功能.默认情况下,您可以将<img>标签拖放到任何地方; 默认的拖放操作就像人们梦寐以求的那样.
所以,考虑一下这种默认行为如何对我们的<img>伙伴们如此有用- 我只想稍微扩展这种行为以包含更多的HTML - 这似乎应该是容易实现的.
首先,我<a>使用draggable属性设置我的花哨标签,并禁用contenteditable(不确定是否有必要,但似乎它可能也是关闭):
<a class="fancy" [...] draggable="true" contenteditable="false">
Run Code Online (Sandbox Code Playgroud)
然后,因为用户仍然可以将图像拖出花哨的<a>盒子,我不得不做一些CSS.我在Chrome上工作,所以我只向你展示-webkit-前缀,尽管我也使用了其他的.
.fancy {
-webkit-user-select:none;
-webkit-user-drag:element; }
.fancy>img {
-webkit-user-drag:none; }
Run Code Online (Sandbox Code Playgroud)
现在,用户可以拖动整个花哨的盒子,并且稍微部分褪色的点击拖动表示图像反映了这一点 - 我可以看到我现在正在拿起整个盒子:)
我已经尝试了几种不同CSS属性的组合,上面的组合似乎对我有意义,并且似乎效果最好.
我希望只有这个CSS就足以让浏览器使用整个元素作为可拖动的项目,自动向用户授予我一直梦寐以求的功能...... 然而,它似乎比这更复杂.
这种拖放似乎比它需要的更复杂.
所以,我开始深入研究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');
});
Run Code Online (Sandbox Code Playgroud)
所以这就是我被困的地方:这几乎完全有效.几乎完全.我有一切工作,直到最后.在drop事件中,我现在可以访问我想要在放置位置插入的奇特框的HTML内容.我现在需要做的就是将它插入正确的位置!
问题是我无法找到正确的放置位置,或以任何方式插入它.我一直希望找到某种'dropLocation'对象来转储我的花哨的盒子dropEvent.dropLocation.content=myFancyBoxHTML;,或者至少是某种类型的drop location值,用它来找到我自己的方式将内容放在那里?
我给了什么吗?
我完全错了吗?我完全错过了什么吗?
我试着按照document.execCommand('insertHTML',false,content);我的预期使用,但不幸的是我在这里失败了,因为我希望选择插入符不在精确的放置位置.
我发现,如果我注释掉所有event.preventDefault();的,那么选择插入符号就会变得可见,并且正如人们所希望的那样,当用户准备放下时,将它们的拖拽悬停在满足者身上时,可以看到在角色之间运行的小选择插入符号跟随用户的光标和放下操作 - 向用户指示选择插入符表示精确的放置位置.我需要这个选择插入符号的位置.
通过一些实验,我在drop事件期间尝试了execCommand-insertHTML',并且dragend事件 - 既不插入drop-selection-caret所在的HTML,而是使用在拖动操作之前选择的任何位置.
因为选择插入符号在dragover期间是可见的,所以我制定了一个计划.
有一段时间,我试图在dragover事件中插入一个临时标记,就像<span class="selection-marker">|</span>之后一样,$('.selection-marker').remove();试图浏览器不断地(在dragover期间)删除所有选择标记,然后在插入点添加一个 -在任何时刻,无论何处插入点,基本上都会留下一个标记.当然,计划是用我所拥有的拖动内容替换这个临时标记.
当然,这些都不起作用:我无法按照计划将选择标记插入到明显可见的选择插入符号中 - 再次,在拖动操作之前,execCommand-insertedHTML将自己放置在选择插入符所在的任何位置.
如何获取或插入拖放操作的精确位置? 我觉得这显然是拖放操作中的常见操作 - 当然我一定忽略了某种重要且明显的细节?我是否还需要深入了解JavaScript,或者有一种方法可以使用draggable,droppable,contenteditable和一些奇特的CSS3这样的属性来做到这一点?
我还在狩猎 - 仍然在修补 - 我会在发现我失败的时候立即回复:)
Farrukh发布了一个很好的建议 - 使用:
console.log( window.getSelection().getRangeAt(0) );
Run Code Online (Sandbox Code Playgroud)
要查看选择插入符的实际位置.我把它放到了dragover事件中,当我想到选择插入符号在我满意的可编辑内容之间可见时.
唉,返回的Range对象报告在拖放操作之前属于选择插入符的偏移索引.
这是一种勇敢的努力.谢谢Farrukh.
那么这里发生了什么?我感觉到我看到的那个小小的选择插入符号,并不是选择插入符号!我认为这是一个冒名顶替者!
事实证明,这是一个冒名顶替者!在实际选择尖整个拖动操作过程中保持到位!你可以看到小虫子!
我正在阅读MDN Drag and Drop Docs,并发现了这个:
当然,您可能还需要围绕dragover事件移动插入标记.您可以像使用其他鼠标事件一样使用事件的clientX和clientY属性来确定鼠标指针的位置.
哎呀,这是否意味着我应该为自己解决这个问题,基于clientX和clientY?使用鼠标坐标自己确定选择插入符的位置?害怕!!
我明天会这样做 - 除非我自己,或者其他人在这里阅读,否则可以找到一个理智的解决方案:)
Cha*_*kal 39
我做了一个荒谬的摆弄.所以,jsFiddling这么多.
这不是一个强大的或完整的解决方案; 我可能永远不会想出一个.如果有人有任何更好的解决方案,我会全力以赴 - 我不想这样做,但这是我迄今为止唯一能够发现的方式.下面的jsFiddle,以及我即将呕吐的信息,在我特定的WAMP设置和计算机上使用我的特定版本的Firefox和Chrome,在这个特定的实例中为我工作. 如果它在您的网站上无效,请不要向我哭泣.这个拖放垃圾显然是每个人都为自己.
jsFiddle:Chase Moskal的Dragon Drop
所以,我的女朋友的脑子很无聊,她觉得我真的说"龙滴",我只是说"拖放".它卡住了,所以我称之为我为处理这些拖放情况而创建的小JavaScript伙伴.
事实证明 - 这有点像噩梦.即使乍一看,HTML5拖放API也很糟糕.然后,当你开始理解并接受它应该工作的方式时,你几乎要热身了.然后你意识到它实际上是一个多么可怕的噩梦,因为你了解Firefox和Chrome如何在他们自己的特殊情况下使用这个规范方式,似乎完全忽略了你的所有需求.你会发现自己会问这样的问题:"等等,现在甚至被拖动了什么元素?如何获取该信息?如何取消此拖动操作?如何阻止此特定浏览器对此情况的唯一默认处理?" ...你的问题的答案:"你是独立的,失败!继续黑客入侵,直到有效!".
所以,这就是我如何在多个contenteditable的内部,周围和之间 完成精确拖放任意HTML元素的方法.(注意:我不会完全深入了解每一个细节,你必须要看看jsFiddle - 我只是漫无目的地从我的经历中记得看似相关的细节,因为我的时间有限)
user-select:none; user-drag:element;在精美的盒子上,然后特别user-drag:none;是在花式盒子里的图像(以及任何其他元素,为什么不呢?).不幸的是,对于Firefox来说这还不够,它要求draggable="false"在图像上明确设置属性以防止它被拖动.draggable="true"和dropzone="copy"到contenteditables.对于draggables(fancyboxes),我绑定了一个处理程序dragstart.我们设置dataTransfer来复制一个空白的HTML''字符串 - 因为我们需要欺骗它以为我们要拖动HTML,但是我们要取消任何默认行为.有时默认行为会以某种方式滑落,并且会导致重复(因为我们自己进行插入),所以现在最糟糕的故障是在拖动失败时插入'(空格).我们不能依赖默认行为,因为它经常失败,所以我发现这是最通用的解决方案.
DD.$draggables.off('dragstart').on('dragstart',function(event){
var e=event.originalEvent;
$(e.target).removeAttr('dragged');
var dt=e.dataTransfer,
content=e.target.outerHTML;
var is_draggable = DD.$draggables.is(e.target);
if (is_draggable) {
dt.effectAllowed = 'copy';
dt.setData('text/plain',' ');
DD.dropLoad=content;
$(e.target).attr('dragged','dragged');
}
});
Run Code Online (Sandbox Code Playgroud)
对于dropzones,我为dragleave和绑定了一个处理程序drop.dragleave处理程序仅适用于Firefox,因为在Firefox中,当您尝试将其拖动到contenteditable之外时,拖放功能会起作用(默认情况下Chrome会拒绝您),因此它会对Firefox进行快速检查relatedTarget.一怒之下.
Chrome和Firefox有不同的方式来获取Range对象,因此必须为drop事件中的每个浏览器进行不同的操作.Chrome根据鼠标坐标 建立了一个范围(是的,这是正确的),但Firefox在事件数据中提供了它.document.execCommand('insertHTML',false,blah)原来是我们如何处理下降.OH,我忘了提 - 我们不能dataTransfer.getData()在Chrome上使用我们的dragstart设置HTML - 它似乎是规范中的某种奇怪的错误.Firefox在它的标记上调用了规范,并且反过来给了我们数据 - 但Chrome没有,所以我们向后弯曲并将内容设置为全局,并通过地狱来杀死所有默认行为......
DD.$dropzones.off('dragleave').on('dragleave',function(event){
var e=event.originalEvent;
var dt=e.dataTransfer;
var relatedTarget_is_dropzone = DD.$dropzones.is(e.relatedTarget);
var relatedTarget_within_dropzone = DD.$dropzones.has(e.relatedTarget).length>0;
var acceptable = relatedTarget_is_dropzone||relatedTarget_within_dropzone;
if (!acceptable) {
dt.dropEffect='none';
dt.effectAllowed='null';
}
});
DD.$dropzones.off('drop').on('drop',function(event){
var e=event.originalEvent;
if (!DD.dropLoad) return false;
var range=null;
if (document.caretRangeFromPoint) { // Chrome
range=document.caretRangeFromPoint(e.clientX,e.clientY);
}
else if (e.rangeParent) { // Firefox
range=document.createRange(); range.setStart(e.rangeParent,e.rangeOffset);
}
var sel = window.getSelection();
sel.removeAllRanges(); sel.addRange(range);
$(sel.anchorNode).closest(DD.$dropzones.selector).get(0).focus(); // essential
document.execCommand('insertHTML',false,'<param name="dragonDropMarker" />'+DD.dropLoad);
sel.removeAllRanges();
// verification with dragonDropMarker
var $DDM=$('param[name="dragonDropMarker"]');
var insertSuccess = $DDM.length>0;
if (insertSuccess) {
$(DD.$draggables.selector).filter('[dragged]').remove();
$DDM.remove();
}
DD.dropLoad=null;
DD.bindDraggables();
e.preventDefault();
});
Run Code Online (Sandbox Code Playgroud)
好的,我厌倦了这一点.我已经写了所有我想要的内容.我把它称为一天,如果我想到任何重要的事情,可能会更新它.
谢谢大家.//追.