0

I try to create a piece of code in javascript to add a node ("span", "strong"...) in a contenteditable "div" where the caret is (when I click on a "button").

I started thinking about the code like this (it doesn't work, it's just a start): http://jsfiddle.net/Md8KX/2/

HTML:

<div id="myDiv" contenteditable="true">
    <strong>Test:</strong> This is a <strong>really</strong> good test.
</div>
<button type="button" id="myButton">Add a span</button>

CSS:

#myDiv{padding:15px;border:solid 1px #000;}

JS:

// Variable to stock the caret position
var caretPosition = 0;

// Event to get caret position on keyup and on blur
$('#myDiv').on('keyup focus', function () {
    caretPosition = getCaretPosition(this);
});

// Event to add node when click on button
$('#myButton').on('click', function (e) {
    e.preventDefault();
    addNode(this, caretPosition, '<span>Node</span>');
});

// Function to get caret position with 1 param : the editable box
var getCaretPosition = function (editableBox) {
    var position = 0;
    // TO DO : get caret position
    return position;
}

// Function to add node with 3 params : the editable box, the caret position and the node to add
var addNode = function (editableBox, caretPosition, nodeToAdd) {
    // TO DO : add node in element
}

But I have absolutely no idea to get the cursor position and then insert a node at this point :) I read things about the "range" property in javascript or "createTextRange", but I'm not sure I understand and I don't know if this is the best way to do it.

Have you any idea to do this or a track that could help me to start please? Thank you in advance!

Baptiste D.
  • 105
  • 1
  • 11
  • Here is my [**`fiddle`**](http://jsfiddle.net/Vedant_Terkar/LZZ5w/3/) For a similar [**`question`**](http://stackoverflow.com/questions/22833115/how-can-i-format-the-text-inside-input-type-text-to-create-a-bounding-box-t/22834803#22834803). Hope it'll help you. – Vedant Terkar May 12 '14 at 16:50
  • 1
    http://stackoverflow.com/a/6691294/96100 – Tim Down May 12 '14 at 22:24
  • @TimDown : it works well, thank you! But there is still a problem: if in the meantime another DOM element takes focus (or if contenteditable loses focus), the node will not be added to the caret position but at the beginning of the div. Do you have an idea to save the caret position and keep it in a variable to avoid this problem? – Baptiste D. May 13 '14 at 08:39
  • 1
    Just save the range before focus is lost. Example: http://stackoverflow.com/questions/4687808/contenteditable-selected-text-save-and-restore – Tim Down May 13 '14 at 10:47

2 Answers2

0

Old post, I get it. But this helped me quite a bit. I found this solution somewhere, sorry I cannot remember or I would give credit.

function pasteHtmlAtCaret(html) {
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
        // non-standard and not supported in all 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);
        }
        range.insertNode(frag);

        // Preserve the selection
        if (lastNode) {
            range = range.cloneRange();
            range.setStartAfter(lastNode);
            range.collapse(true);
            sel.removeAllRanges();
            sel.addRange(range);
        }
    }
} else if (document.selection && document.selection.type != "Control") {
    // IE < 9
    document.selection.createRange().pasteHTML(html);
}
}
Robert Kehoe
  • 421
  • 5
  • 7
-1

I've created a working example for this, while not perfect I hope it work as a base to what you want to achieve. This can be found here: http://jsbin.com/jociy/2.

This is the source code and it's self explanatory:

$(document).ready(function(){

  // helper function credit to http://stackoverflow.com/questions/3972014/get-caret-position-in-contenteditable-div
  function getCaretPosition(editableDiv) {
    var caretPos = 0, containerEl = null, sel, range;
    if (window.getSelection) {
        sel = window.getSelection();
        if (sel.rangeCount) {
            range = sel.getRangeAt(0);
            if (range.commonAncestorContainer.parentNode == editableDiv) {
                caretPos = range.endOffset;
            }
        }
    } else if (document.selection && document.selection.createRange) {
        range = document.selection.createRange();
        if (range.parentElement() == editableDiv) {
            var tempEl = document.createElement("span");
            editableDiv.insertBefore(tempEl, editableDiv.firstChild);
            var tempRange = range.duplicate();
            tempRange.moveToElementText(tempEl);
            tempRange.setEndPoint("EndToEnd", range);
            caretPos = tempRange.text.length;
        }
    }
    return caretPos;
  }

  // listener will keep track of caret position
  var position = 0;
  $('#myDiv').keyup(function() { 

     position = getCaretPosition(this);

  });

  // the special span element to insert
  var mySpan = "<span class='mySpan'>Yes!</span>";

  $("#myButton").on("click", function(){

    // sorry about this, this can and should be refactored
    var data1 = $("#myDiv").text().slice(0, position);
    var data2 = $("#myDiv").text().slice(position,$("#myDiv").text().length);

    $("#myDiv").html("<span>" + data1 + mySpan + data2 + "</span>");

  });


});

HTML elements for this example:

  <div id="myDiv" class="yellow foo" contenteditable="true">Zombie ipsum reversus ab viral inferno, nam rick grimes malum cerebro. De carne lumbering animata corpora quaeritis. Summus brains sit , morbo vel maleficia? De apocalypsi gorger omero undead survivor dictum mauris.</div>

  <button type="button" id="myButton">Add SPAN</button>

Some styles if you wish:

.foo {
  font-family: arial, sans-serif;
  margin: 10px;
  padding: 25px;
  opacity: 0.6;
}
.yellow {
  background-color: yellow;
  color: #000;
}
button {
  padding: 20px;
  margin-left: 10px;
  border: none;
  background-color: #ccc;
  color: #fff;
  font-weight: bold;
  text-shadow: 1px 1px 0px #888;
  text-transform: uppercase;
  cursor: pointer;
}
button:hover {
  opacity: 0.6;
}
.mySpan {
 background-color: red;
 color: yellow; 
}

Hope this helps!

punkbit
  • 7,347
  • 10
  • 55
  • 89
  • Unfortunately this is not what I need :) In your example, you use the "text ()" property that doesn't allow multiple nodes simultaneously in the contenteditable. I think we should use the "html ()" property and the property "childNodes", but I don't know how :) But thank you for the help! – Baptiste D. May 12 '14 at 17:54