【问题标题】:Drag-n-Drop on contentEditable elements在 contentEditable 元素上拖放
【发布时间】:2011-03-01 16:49:45
【问题描述】:

互联网上有许多所见即所得的编辑器,但我还没有找到一个实现某种形式的拖放实现。

创建自己的编辑器很容易,但我希望用户能够从可编辑区域外拖动元素(即标记),并让他们将其拖放到可编辑区域内他们选择的位置。

在可编辑元素的特定位置注入 html 很容易,但是当用户在可编辑区域中的某个元素上拖动 DIV 时,如何确定插入符号的位置。为了更好地说明我要解释的内容,请参见以下场景。

可编辑区域(无论是处于编辑模式的 IFRAME 还是其 contentEditable 属性设置为 true 的 DIV)已经包含以下文本:

“亲爱的,请注意……”

用户现在从元素列表中拖出一个表示某个标记的元素到可编辑区域上,将光标移动到文本上,直到插入符号出现在文本中的逗号 (,) 之前,如上所示。当用户在该位置释放鼠标按钮时,将注入 HTML,这可能会导致如下结果:

“亲爱的 {UserFirstName},请注意……”。

我不知道是否有人做过类似的事情,或者至少知道如何使用 JavaScript 来做这件事。

任何帮助将不胜感激。

【问题讨论】:

    标签: javascript drag-and-drop contenteditable


    【解决方案1】:

    这是我解决可编辑元素上的自定义拖动元素问题的方法。最大的问题是当鼠标悬停在可编辑元素上时,无法确定鼠标光标的文本偏移量。我曾尝试假装单击鼠标以将插入符号设置在所需位置,但这不起作用。即使这样做了,在拖动时也不会直观地看到插入符号的位置,而只会看到最终的拖放。

    由于可以将鼠标悬停事件绑定到元素而不是文本节点,因此可以将可编辑元素设置为暂时不可编辑。查找所有元素并将每个文本节点包装在一个跨度中,以免破坏文本流。每个 span 都应该有一个类名,以便我们可以再次找到它们。

    包装之后,应该再次找到所有包装的文本节点,并将每个字符与另一个具有类名的跨度包装在一起,以便再次找到它们。

    使用事件委托可以向主可编辑元素添加一个事件,该事件将为每个字符范围应用一种样式,以显示插入符号,一个闪烁的 GIF 图像作为背景。

    同样,使用事件委托,应该为每个字符上的鼠标向上事件(放下事件)添加一个事件。现在可以使用字符跨度在其父级(包装的文本节点)中的位置(偏移量)来确定偏移量。现在可以撤消所有包装,保留对计算偏移量的引用,并在撤消包装时保留对适用文本节点的引用。

    使用浏览器的范围和选择对象,现在可以使用计算的偏移量设置选择到适用的文本节点,并在新设置的选择(插入符号位置)处注入所需的 HTML,等等!

    下面是一个使用 jQuery 的 sn-p,它将找到文本节点,包装它们:

    editElement.find("*:not(.text-node)").contents().filter(function(){ 
        return this.nodeType != 1;
    }).wrap("<span class=\"text-node\"/>");
    

    要查找每个文本节点并包装每个字符,请使用:

    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);
    });
    

    希望这种方法可以帮助那些面临类似挑战的人

    【讨论】:

    • 可能不需要像上面提到的那样在字符跨度上应用委托的 mouseup 事件,可以使用 droppable 事件的原始事件属性来确定实际上是否已将可拖动事件拖放到字符跨度上.仅供参考....
    【解决方案2】:

    如果我明白你在说什么,那么这只是基本的拖放操作。下面的解决方案应该接近 FIREFOX 的最佳答案。我敢肯定 IE 有不同的功能。如需更多帮助,请转至http://www.html5rocks.com/en/tutorials/dnd/basics/

    设置要拖动的对象的“可拖动”属性,并将对象的“ondragstart”方法设置为“dragStartHandler”或您调用的任何函数。

    // You can set this to 'text/plain' if you don't want to insert HTML content
    var internalDNDType = 'text/html';
    
    function dragStartHandler(event) {
      // This is whatever html data you want to insert.
      var textContent = "<b>"+userFirstName+"</b>";
    
      event.dataTransfer.setData(internalDNDType, textContent);
      event.dataTransfer.effectAllowed = 'copy';
    }
    
    function dragEnterHandler(event)
    {
      event.preventDefault();
      return false;
    }
    
    function dragOverHandler(event)
    {
      event.preventDefault();
      return false;
    }
    
    function dropHandler(event) {
      event.preventDefault();
      event.stopPropogation();
      return false;
    }
    

    【讨论】:

    • 感谢您的回复,但是这依赖于 HTML5,并且应用程序必须支持旧浏览器。对于不支持旧版浏览器的新更新应用程序,此方法最适合。
    【解决方案3】:

    目前正在开发一个 HTML5 API 来做到这一点,但不幸的是 IE 不支持它。 编辑:显然 IE 确实支持拖放,但我不太熟悉如何有用。试试Googling“IE拖放”。

    尝试查看以下网站:

    【讨论】:

    • 我的一位同事找到了解决方案,我向他提出了问题。我很快就会在这里发布解决方案
    • 唉,他的解决方案并不适用。狩猎又开始了……grrr
    • @Raybiez 也许您可以编辑您的问题以添加不起作用的解决方案?这也会产生将这个问题推回首页的副作用。
    猜你喜欢
    • 2011-12-23
    • 1970-01-01
    • 2013-01-18
    • 2018-09-29
    • 2015-03-18
    • 1970-01-01
    • 2011-06-24
    • 1970-01-01
    • 2015-08-28
    相关资源
    最近更新 更多