在 contentEditable 元素上拖放
互联网上有许多所见即所得的编辑器,但我还没有找到一个实现某种形式的拖放实现.
There are numerous WYSIWYG editors available on the internet, but I'm yet to find one that implements some form of drag-n-drop implementation.
创建自己的编辑器很容易,但我希望用户能够从可编辑区域外拖动元素(即标记),并让他们将其放置在可编辑区域内他们选择的位置.
It is easy to create one's own editor, but I want to the user to be able to drag elements (ie. tokens) from outside the editable area and have them drop it at a location of their choice inside the editable area.
在可编辑元素的特定位置注入 html 很容易,但是当用户在可编辑区域中的某个元素上拖动 DIV 时,如何确定插入符号的位置.为了更好地说明我要解释的内容,请参见以下场景.
It is easy to inject html at a specific location of an editable element, but how do one determine where the caret should be when the user is dragging a DIV over some element in the editable area. To better illustrate what I'm trying to explain, see the following scenario.
可编辑区域(处于编辑模式的 IFRAME 或 contentEditable 属性设置为 true 的 DIV)已包含以下文本:
The editable area (either an IFRAME in edit mode or a DIV with its contentEditable attribute set to true) already contains the following text:
亲爱的,请注意……"
用户现在从元素列表中将一个表示某个标记的元素拖到可编辑区域上,将光标移动到文本上,直到插入符号出现在文本中的逗号 (,) 之前,如上所示.当用户在该位置释放鼠标按钮时,将注入 HTML,这可能会导致如下结果:
The user now drags an element representing some token from a list of elements, over the editable area, moving the cursor over the text until the caret appear just before the comma (,) in the text as shown above. When the user releases the mouse button at that location, HTML will be injected which could result in something like this:
亲爱的 {UserFirstName},请注意……".
"Dear {UserFirstName}, please take note of ...".
我不知道是否有人做过类似的事情,或者至少知道如何使用 JavaScript 来做这件事.
I do not know if anyone has ever done anything similar to this, or at least know of how one would go about doing this using JavaScript.
任何帮助将不胜感激.
推荐答案
这是我解决可编辑元素上的自定义拖动元素问题的方法.最大的问题是当鼠标悬停在可编辑元素上时,无法确定鼠标光标的文本偏移量.我曾尝试假装单击鼠标以将插入符号设置在所需位置,但这不起作用.即使这样做了,在拖动时也不会直观地看到插入符号的位置,而只会看到结果拖放.
Here is my approach to solving the issue of custom drag elements on editable elements. The big issue is that one cannot determine the text offset of the mouse cursor when hovering over the editable element. I have tried faking a mouse click to set the caret at the desired position but that did not work. Even if it did, one would not visually see the placement of the caret while dragging, but only the resulting drop.
由于可以将鼠标悬停事件绑定到元素而不是文本节点,因此可以将可编辑元素设置为暂时不可编辑.查找所有元素并将每个文本节点包装在一个跨度中,以免破坏文本流.每个 span 都应该有一个类名,以便我们可以再次找到它们.
Since one can bind mouse-over events to elements and not text-nodes, one can set the editable element to be temporarily un-editable. Find all elements and wrap each text-node in a span as to not breaking the flow of the text. Each span should be given a classname so we can find them again.
包装之后,应该再次找到所有包装的文本节点,并将每个字符与另一个带有类名的跨度包装在一起,以便再次找到它们.
After the wrapping, one should again find all the wrapped text-nodes and wrap each character with another span with a classname that one can find them again.
使用事件委托可以向主可编辑元素添加一个事件,该事件将为每个字符范围应用一种样式,以显示插入符号,一个闪烁的 GIF 图像作为背景.
Using event delegation one can add an event to the main editable element that will apply a style to each character span that will display the caret, a blinking GIF image as a background.
同样,使用事件委托,应该为每个字符上的鼠标向上事件(放下事件)添加一个事件.现在可以使用字符跨度在其父级(包装的文本节点)中的位置(偏移量)来确定偏移量.现在可以撤消所有包装,保留对计算偏移量的引用,并在撤消包装时保留对适用文本节点的引用.
Again, using event delegation, one should add an event for the mouse-up event (drop event) on each character. One can now determine the offset using the character span's position (offset) within its parent (wrapped text-node). One can now undo all the wrapping, keeping a reference to the calculated offset and while undoing the wrapping keeping a reference to the applicable text-node.
使用范围 &浏览器的选择对象,现在可以使用计算的偏移量设置选择到适用的文本节点,并在新设置的选择(插入符号位置)处注入所需的 HTML,等等!
Using the range & selection objects of the browser, one can now set the selection using the calculated offset to the applicable text-node and inject the required HTML at the newly set selection (caret position), et viola!
下面是一个使用 jQuery 的片段,它将找到文本节点,将它们包装起来:
Here follows a snippet using jQuery that will find textnodes, wrap them:
editElement.find("*:not(.text-node)").contents().filter(function(){
return this.nodeType != 1;
}).wrap("<span class="text-node"/>");
要查找每个文本节点并包装每个字符,请使用:
To find each text-node and wrap each character, use:
editElement.find(".text-node").each(function()
{
var textnode = $(this), text = textnode.text(), result = [];
for (var i = 0; i < text.length; i++) result.push(text.substr(i, 1));
textnode.html("<span class="char">"
+ result.join("</span><span class="char">") + "</span>");
});
要撤消包装:
editElement.find(".text-node").each(function()
{
this.parentNode.replaceChild(document.createTextNode($(this).text()), this);
});
希望这种方法可以帮助那些面临类似挑战的人
Hope this approach helps those having similar challenges
相关文章