1

I am trying to make my WYSIWYG editor same as Medium's. When someone pastes a paragraph of text, it should get into a <p> element right below the contentEditable element and at the position where the caret is. It shouldn't get nested inside the element where we pasted.

As of now, it is getting nested.

$('#content').on('paste', function(e){
    
    e.preventDefault(); // dont paste right away
    
    console.log(e.originalEvent.clipboardData.getData('text/plain'));
    
    var re = new RegExp('[\n\r]+', 'g');
    
    var pasteContent = e.originalEvent.clipboardData.getData('text/plain').replace(re, '</p><p>');  // separate all paras
    pasteContent = '</p><p>' + pasteContent + '</p><p>';
    
    re = new RegExp('<p><\/p>', 'g');  // clean empty paras
    pasteContent = pasteContent.replace(re, '');
    
    console.log(pasteContent);
    
    pasteHtmlAtCaret(pasteContent, true);
    
});


function pasteHtmlAtCaret(html, selectPastedContent) { // source: http://stackoverflow.com/a/6691294/586051
    var sel, range;
    if (window.getSelection) {
        // IE9 and non-IE
        sel = window.getSelection();
        if (sel.getRangeAt && sel.rangeCount) {
            range = sel.getRangeAt(0);
            range.deleteContents();
            
            // Range.createContextualFragment() would be useful here but is
            // only relatively recently standardized and is not supported in
            // some browsers (IE9, for one)
            var el = document.createElement("div");
            el.innerHTML = html;
            var frag = document.createDocumentFragment(), node, lastNode;
            while ( (node = el.firstChild) ) {
                lastNode = frag.appendChild(node);
            }
            var firstNode = frag.firstChild;
            range.insertNode(frag);
            
            // Preserve the selection
            if (lastNode) {
                range = range.cloneRange();
                range.setStartAfter(lastNode);
                if (selectPastedContent) {
                    range.setStartBefore(firstNode);
                } else {
                    range.collapse(true);
                }
                sel.removeAllRanges();
                sel.addRange(range);
            }
        }
    } else if ( (sel = document.selection) && sel.type != "Control") {
        // IE < 9
        var originalRange = sel.createRange();
        originalRange.collapse(true);
        sel.createRange().pasteHTML(html);
        if (selectPastedContent) {
            range = sel.createRange();
            range.setEndPoint("StartToStart", originalRange);
            range.select();
        }
    }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id='content' contentEditable=true><p>test1</p><p>test2</p><p>test3</p><p>test4</p></div>

How do I fix this?

Rahul Desai
  • 15,242
  • 19
  • 83
  • 138

1 Answers1

2

Have you tried using document.execCommand('insertHTML') instead of trying to paste the text in yourself?

If the user has some text selected, calling this should trigger the browser to replace the current selection with whatever you pass as an argument.

So instead of:

pasteHtmlAtCaret(pasteContent, true);

try

doc.execCommand('insertHTML', false, pasteContent);

Depending on your supported browsers, this might work across the board. You can verify that a browser supports this command via:

doc.queryCommandSupported('insertHTML')

If that returns true for all your browsers, then you should be good to go. If not, you can look at this post for fallback code: Insert html at caret in a contenteditable div

It's a solution by Tim Down, and it seems like you're already using it in your solution so perhaps there was a copy/paste mistake?

Community
  • 1
  • 1
Nate Mielnik
  • 571
  • 2
  • 7