Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HTML5 draggable elements within contenteditable div - stops working after first drop - why?

I am trying to create a few draggable elements (tokens) that can be dragged around inside a contenteditable div. It seems that everything is working except that... after I drag one element and drop it, I cannot drag it again. It seems that I cannot bind to it's dragstart event again.

Any idea why this happens and how can i fix it?

Here's a link to my fiddle: http://jsfiddle.net/gXScu/1/

HTML:

<div id="editor" contenteditable="true">
    Testime siinkohal seda, et kuidas<br />
    on võimalik asja testida.
    <span class="draggable" draggable="true" contenteditable="false">Token</span>
</div>
<span class="draggable" draggable="true" contenteditable="false">Token 2</span>

Javascript (jQuery)

var bindDraggables = function() {
    console.log('binding draggables', $('.draggable').length);
    $('.draggable').off('dragstart').on('dragstart', function(e) {
        if (!e.target.id)
            e.target.id = (new Date()).getTime();
        e.originalEvent.dataTransfer.setData('text/html', e.target.outerHTML);
        console.log('started dragging');
        $(e.target).addClass('dragged');
    }).on('click', function() {
        console.log('there was a click');
    });
}

$('#editor').on('dragover', function (e) {
    e.preventDefault();
    return false;
});

$('#editor').on('drop', function(e) {
    e.preventDefault();
    var e = e.originalEvent;
    var content = e.dataTransfer.getData('text/html');
    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);
  }
    console.log('range', range)
  var sel = window.getSelection();
  sel.removeAllRanges(); sel.addRange(range);

  $('#editor').get(0).focus(); // essential
  document.execCommand('insertHTML',false, content);
    //$('#editor').append(content);
  sel.removeAllRanges();
    bindDraggables();
    console.log($('[dragged="dragged"]').length);
    $('.dragged').remove();
});

bindDraggables();

CSS:

#editor {
    border: 2px solid red;
    padding: 5px;
}
.draggable {
    display: inline-block;
    padding: 3px;
    background: yellow;
    cursor: move !important;
}
like image 832
ragulka Avatar asked Jun 03 '13 15:06

ragulka


2 Answers

I also tried a few hacks but none of them seems to work. I was inspecting HTML and I found that in your example the newly inserted content don't have any contenteditable attribute assigned and when I assigned to it manually then it started working fine.

Here is my code http://jsfiddle.net/gXScu/8/

I just added this line

$('.draggable').attr("contenteditable", false); in bindDraggables method.

I agree with Reinmar explanation over contenteditable.

like image 133
Imran Latif Avatar answered Oct 07 '22 03:10

Imran Latif


Apparently Chrome has a bug (not a surprise - contenteditable is the most buggy feature in all browsers). Your code works perfectly fine on Firefox, but for some reason element inserted into editable with insertHTML command cannot be dragged any more.

I tried few hacks, but only one worked - inserting other element and then replacing it.

So instead:

document.execCommand('insertHTML', false, content);

Use something like this:

var spanId = 'temp-' + (new Date()).getTime();

// Insert span with zero-width space (so it isn't visible and it isn't empty).
document.execCommand('insertHTML', false, '<span id="' + spanId + '">\u200b</span>');
// Replace that span with our dragged content.
$('#' + spanId).replaceWith(content);

You can try this here: http://jsfiddle.net/gXScu/5/. I also corrected wrong order of this:

$('.dragged').remove();
bindDraggables();
like image 28
Reinmar Avatar answered Oct 07 '22 01:10

Reinmar