Search code examples
javascriptjqueryrangeselection

selection on editable div doesn't work if an Enter is typed


I have an editable div and multiple blocks of texts between a starting three backticks and ending three backticks markers
Let's say the cursor is anywhere between the markers
Clicking on a button I need to select entire current block

This code works until I type Enter anywhere inside the current block
Also - I believe there is a shorter version of the code

function get_left(){
let s = window.getSelection();
let range = s.getRangeAt(0);
let so = range.startOffset;
let textNode = range.startContainer;
let textContent = textNode.textContent;
let startPosition = Math.max(so - 4, 0);
let a = textContent.substring(startPosition, so);
return a;
}


function go_left(){
    let par = document.getElementById("cstory");
  let s = window.getSelection();
  let range = document.createRange();
  let cp = window.getSelection().getRangeAt(0).startOffset;
  let textNode = par.firstChild;
    range.setStart(textNode, cp - 1);
    range.setEnd(textNode, cp - 1);
    s.removeAllRanges();
    s.addRange(range);
}

$('button').on('click', async function(){
    while(get_left() !== "```\n"){go_left();}
    let s = window.getSelection();
    let range = s.getRangeAt(0);
    let story = document.getElementById("cstory").innerText;
    let nextXIndex = story.indexOf("\n```", range.endOffset);
    if(nextXIndex !== -1){
        range.setEnd(range.endContainer, nextXIndex);
        s.removeAllRanges();
        s.addRange(range);
    }
});
.cstory{white-space:pre-wrap;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class='cstory' id='cstory' contenteditable>
lorem sea
```
lorem ipsum
dolor sit
sky
```
lorem sky

```
lorem ipsum
dolor sit
sky
```
dolor sit
</div>
<br>
<button>CLICK</button>


Solution

  • no additional libraries are needed. plain JS solution here, preventing divs creation and replacing the double quotes so it can work smoothly, as the requirement is to select text which is between backticks, double quotes were disrupting the format and selections were not working

    document.querySelector('div[contenteditable]').addEventListener('keydown', function(e) {
      // creating a new selection element
      let sltcn = window.getSelection()
      // identifying the current position of the cursor within the text
      let range = sltcn.getRangeAt(0)
      // saveing the current cursor position in a variable
      let position = range.startOffset
    
      if (event.key === 'Enter') {
        document.execCommand('insertLineBreak');
        
        // getting the content of the inner HTML of the 'cstory' element and removing any quotation marks
        let content = document.getElementById("cstory").innerHTML;
        content = content.replace(/"/g, "");
        // updating the content without quotation marks
        document.getElementById("cstory").innerHTML = content;
        
         // checking if the cursor is at the end of the text
        if (position === range.endOffset) {
          // creating a new range at the start of the next paragraph
          let newRange = document.createRange();
          newRange.setStart(document.getElementById("cstory").childNodes[0], position + 1);
          newRange.collapse(true);
          
          sltcn.removeAllRanges();
          sltcn.addRange(newRange);
        } else {
          // cursor is in the middle of the line, moving it to the end
          range.setStart(document.getElementById("cstory").childNodes[0], position + 1);
          sltcn.removeAllRanges();
          sltcn.addRange(range);
        }
        
        event.preventDefault()
      }
    });
    
    function clsl() {
      // getting the 'cstory' element
      let cstory = document.getElementById("cstory");
    
      // Getting the cursor position within the div
      let crpos = window.getSelection().getRangeAt(0).startOffset;
      
      // getting the text content
      let cont = cstory.textContent;
    
      // getting the position of the last 3 backticks
      let firstticks = cont.lastIndexOf("```", crpos-1);
    
      // getting the position of the first 3 backticks
      let lastticks = cont.indexOf("```", crpos);
    
      // If both starting and ending backticks are found, selecting the text between them
      if (firstticks !== -1 && lastticks !== -1){
        // Setting the selection range to start after the first backtick and end before the last backtick
        let sltcn = window.getSelection();
        let range = document.createRange();
        // Setting the start and end of the range to include the text between the backticks
        range.setStart(cstory.childNodes[0], firstticks+4);
        range.setEnd(cstory.childNodes[0], lastticks);
        sltcn.removeAllRanges();
        sltcn.addRange(range);
    
      }
    }
    .cstory{white-space:pre-wrap;}
    <div class='cstory' id='cstory' contenteditable>
    lorem sea
    ```
    lorem ipsum
    dolor sit
    sky
    ```
    lorem sky
    
    ```
    lorem ipsum
    dolor sit
    sky
    ```
    dolor sit
    </div>
    <br>
    <button onclick="clsl()">CLICK</button>