Search code examples
javascripthtmlcsscontenteditable

JavaScript - Add span to each letter written in contenteditable div


I want to create script which add <span> to each letter of contenteditable div. For example:
If you write something in contenteditable div, in HTML code, you will see:

<div contenteditable="true">
    SomeText
</div>

What I have to do to see something like this:

<div contenteditable="true">
    <span>S</span>
    <span>o</span>
    <span>m</span>
    <span>e</span>
    <span>T</span>
    <span>e</span>
    <span>x</span>
    <span>t</span>
</div>  

I wrote some code (using Rangy), but it doesn't work. You can see that code below, but I do not recommend it because it is to long and, as I said, doesn't work.

$('#Box').keypress(function() {
  setTimeout(function() {
    var selLenght = getCaretCharacterOffsetWithin(document.getElementById('Box'));

    var precedingChar = "",
      sel, range, precedingRange;
    sel = rangy.getSelection();
    range = sel.getRangeAt(0).cloneRange();
    range.setStart(document.getElementById('Box'), 0);
    sel.setSingleRange(range);
    sel.removeAllRanges();
    range.setStart(document.getElementById('Box'), selLenght - 21);
    sel.setSingleRange(range);
/*
    var newElem = document.createElement('span');
    newElem.className = 'test';
    $(newElem).html(range.extractContents());
    range.insertNode(newElem);
    $('#MainBox > span:empty').remove();
*/
    rangy.getSelection().move('character', 0);
  });
});

function getCaretCharacterOffsetWithin(element) {
    var caretOffset = 0;
    if (typeof window.getSelection != "undefined") {
        var range = window.getSelection().getRangeAt(0);
        var preCaretRange = range.cloneRange();
        preCaretRange.selectNodeContents(element);
        preCaretRange.setEnd(range.endContainer, range.endOffset);
        caretOffset = preCaretRange.toString().length;
    } else if (typeof document.selection != "undefined" && document.selection.type != "Control") {
        var textRange = document.selection.createRange();
        var preCaretTextRange = document.body.createTextRange();
        preCaretTextRange.moveToElementText(element);
        preCaretTextRange.setEndPoint("EndToEnd", textRange);
        caretOffset = preCaretTextRange.text.length;
    }
    return caretOffset;
}
div{
  width: 400px;
  height: 200px;
  border: 1px solid black;
}
<script src="https://github.com/timdown/rangy/blob/master/lib/rangy-core.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div contenteditable="true" id="Box"></div>

How can I do something like that in JavaScript and/or JQuery and/or free for commercial use external library?
Thanks for help.


Solution

  • UPDATE:

    Using this Github solution I solved the issue of chars appearing on the beginning.

    Does this help you?

    var myDiv = document.getElementById("yourDiv");
    
    function doJob(){
      var myDivContent = myDiv.textContent.trim();
      var myDivContentSplit = myDivContent.split("");
      myDiv.innerHTML = "";
      for(var i=0; i<myDivContentSplit.length ; i++){
        var newSpan = document.createElement('span')
        newSpan.innerHTML = myDivContentSplit[i];
        myDiv.appendChild(newSpan);
      }
      setEndOfContenteditable(myDiv);
    }
    
    myDiv.addEventListener("keyup", doJob);
    
    
    function setEndOfContenteditable(contentEditableElement)
    {
        var range,selection;
        if(document.createRange)//Firefox, Chrome, Opera, Safari, IE 9+
        {
            range = document.createRange();//Create a range (a range is a like the selection but invisible)
            range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range
            range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
            selection = window.getSelection();//get the selection object (allows you to change selection)
            selection.removeAllRanges();//remove any selections already made
            selection.addRange(range);//make the range you have just created the visible selection
        }
        else if(document.selection)//IE 8 and lower
        { 
            range = document.body.createTextRange();//Create a range (a range is a like the selection but invisible)
            range.moveToElementText(contentEditableElement);//Select the entire contents of the element with the range
            range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
            range.select();//Select the range (make it the visible selection
        }
    }
    div{
      border: 1px solid #999;
      padding: 10px;
    }
    span{
      background-color: #ea0;
    }
    <div contenteditable="true" id="yourDiv">
        SomeText
    </div>