1

When you are on discord you can highlight text only using the keyboard like this: **this would be bold text**.

I want to create a similar system in a browser-based text-editor, entirely coded by me, while also adding my own twists, like colors, and comments, like what you would see inside vsc. So it would look like this:

#bold text# /yyellow text/y

What I've came up with:

  1. made the div contenteditable
  2. at every keystroke, I took the text into a variable, and basically just used the .replace() function, putting <span class="...">text</span> everywhere needed. And then just moving the text back onto the page all at once using document.getElementById().innerHTML.

The problem was, that when i reset the div, the caret would return back to the start of the div. So I found Restore cursor position after changing contenteditable on stackoverflow. But it didnt work when I had more than one spans in each other like: <span class="bold"><span class="yellow">text</span></span>

And my last try was using document.execCommand(), but it doesn't work, because you have to highlight the text before.

And because I cant find any more code I could use, I'm asking for help.

UPDATE: Kelly's answer did fix my original issue, but created a new one. I can't input new lines.

I can't press enter, because .innerText doesnt recognize that. br, and div break the caret-fix.

So I'm stuck again.

Penzboti
  • 61
  • 5
  • Hm, well what if you created a span for *each letter* and calculated the styles for each letter. That would certainly make it a lot easier to implement editing. – kelsny Sep 06 '22 at 20:08

1 Answers1

0

<div class="contentEditableText" contenteditable="true">Click me to Edit. Press enter to format. #This formats to bold, for example.#</div>

<script>
function replaceBold(textFieldElement, text){
      const regex = new RegExp("#+[^#]+#+");
    let caretOffset = getCaretIndex(textFieldElement)

    let selectedText = regex.exec(text);
    let newText = text;
    
    while (selectedText != null){
        
        selectedText = selectedText[0];
      replacementText = "<span class=\"bold\">" + selectedText.slice(1, -1) + "</span>";
        
          newText = newText.replace(selectedText, replacementText);
        

          selectedText = regex.exec(newText);
    }
    
   
    textFieldElement.innerHTML = newText;
    
    
    
    
      let textOld = text;
    let textNew = textFieldElement.innerText;
    
    caretOffset = getNewCaretOffset(textOld, textNew, caretOffset);
    setCaret(textFieldElement, caretOffset);
    

    
}


function getNewCaretOffset(textOld, textNew, CaretIndex){
    
    let count = 0;
    
    let i = 0;
    let j = 0;
    CaretIndex = CaretIndex;
    while (i < CaretIndex){
        if ( (textOld[i] != textNew[i]) && (typeof textNew[i] !== "undefined") && (typeof textOld[i] !== "undefined") ){                
          textOld = textOld.replace(textOld[i], '');
          count++;
          CaretIndex = CaretIndex - 1;
          j = j + 1;
          i = 0;
        }
        i++
     }
     CaretIndex = CaretIndex + j;
     
     newOffset = CaretIndex - count;
      
     return newOffset;
}

function getCaretIndex(element) {
  let position = 0;
   
  const selection = window.getSelection();
  if (selection.rangeCount !== 0) {
  const range = window.getSelection().getRangeAt(0);
  const preCaretRange = range.cloneRange();
  preCaretRange.selectNodeContents(element);
  preCaretRange.setEnd(range.endContainer, range.endOffset);
  position = preCaretRange.toString().length;
  }
  
  return position;
}

function setCaret(textFieldElement, offset) {
  let i = 0;
  let node = textFieldElement;
  node.focus();
  let textNode = node.childNodes[0];
  while (offset > textNode.length){
    i += 1;
    offset = offset - textNode.length;
    
    
    textNode = node.childNodes[i];
    
    if (node.childNodes[i].firstChild != null){
        textNode = node.childNodes[i].firstChild;
        
    }
    
  }

  let caret = offset; 
  let range = document.createRange();
  range.setStart(textNode, caret);
  range.setEnd(textNode, caret);
  let sel = window.getSelection();
  sel.removeAllRanges();
  sel.addRange(range);
}

const textField = document.querySelector(".contentEditableText");

textField.addEventListener("keydown", function(e){
    if (e.key === "Enter"){
        e.preventDefault();
    }
});

textField.addEventListener("keyup", function(e){

    if (e.key === "Enter"){
        
        let text = textField.textContent;
      replaceBold(textField, text);
        
    }
    
});
</script>

<style>
.bold {
font-weight: bold;
}
</style>
Raine
  • 26
  • 5
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Sep 12 '22 at 03:18