Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In case of two adjacent editable span and the cursor being in the middle, how html decide on which one i'm writing?

I have this script adding a new span inside an editable div each time the user input a dot in the input text, trying to separate text written in different spans based on the presence of a dot separating them. (I use a custom tag 'mytag' but it behaves actually like a span)

<div style="border:1px solid black;" id='editor-container' contenteditable="true"><mytag id="0">test</mytag></div>

JS:

var divContainer = document.getElementById("editor-container");
var nodeIdIncrement = 0;
var htmlBefore = divContainer.innerHTML; 
var html;
var editedCharIndex;

moveCursorOnDiv("0");

divContainer.addEventListener("input", function(e) {
    html=divContainer.innerHTML;

    editedCharIndex=findFirstDiffPos(htmlBefore,html);
    console.log("html[editedCharIndex]: "+html[editedCharIndex]);

    if(html[editedCharIndex]=="."){
        nodeIdIncrement++; 
      htmlBefore=html.substring(0, editedCharIndex+1)+'</mytag><mytag id="'+nodeIdIncrement+'">'+html.substring(editedCharIndex+1);

      divContainer.innerHTML = htmlBefore;
      moveCursorOnDiv(nodeIdIncrement);
    }else{
        htmlBefore = divContainer.innerHTML;
    }

}, false);

// Find index of newly added character making a diff between previuos situation and present one
function findFirstDiffPos(a, b) {
   var shorterLength = Math.min(a.length, b.length);

   for (var i = 0; i < shorterLength; i++){
       if (a[i] !== b[i]) return i;
   }

   if (a.length !== b.length) return shorterLength;

   return -1;
}

function moveCursorOnDiv(divId){
    console.log("divId: "+divId);
  var el = document.getElementById(divId);
  console.log("inner: "+el.innerHTML);
  var range = document.createRange();
  var sel = window.getSelection();
  range.setStart(el, 0);
  range.collapse(true);
  sel.removeAllRanges();
  sel.addRange(range);
}

Here it is the JSFIDDLE

Let's say I input a char 'A' in the editable div "editor-container" the result will be:

<div style="border:1px solid black;" id="editor-container" contenteditable="true">
   <mytag id="0">A</mytag>
</div>

Then I add a dot. The result will be:

<div style="border:1px solid black;" id="editor-container" contenteditable="true">
   <mytag id="0">A.</mytag>
   <mytag id="1"></mytag>
</div>

When the dot gets added, I force programatically (function moveCursorOnDiv) the cursor to move on the new div. The expected result would be that input an another character 'B' would result in situation:

<div style="border:1px solid black;" id="editor-container" contenteditable="true">
   <mytag id="0">A.</mytag>
   <mytag id="1">B</mytag>
</div>

Instead it actually result in Chrome in:

<div style="border:1px solid black;" id="editor-container" contenteditable="true">
   <mytag id="0">A.B</mytag>
   <mytag id="1"></mytag>
</div>

While in Firefox behaves as expected 90% of times, but sometimes randomly behaves as in Chrome.

So, if I have the cursor between two adjacent span how html decide if the next input will be put in a span or in the other? There's a way to force the expected behaviour?

EDIT: Pretty obviously, this happens only in case the following div is empty for there is ambiguity. If you put a char in the following div and move the caret in position '1' it works like a charm. (Just to say that the problem is not directly dependent from this specific code, which under different conditions works perfectly)

JSFIDDLE 2

This means also the problem can be solved adding something like a &nb sp; when adding a new div, but it not a clean solution.

like image 335
Sasha Grievus Avatar asked Jun 04 '19 10:06

Sasha Grievus


1 Answers

First: You set <div id="editor-container".....> </div> as editable so when you are typing . it is creating a new <mytag> with a new Id but it is not giving an editable option to the new <mytag> and as it is not editable the mouse pointer remains in the same position.

Here I made some changes: Added some border and margin so that it is visible

var divContainer = document.getElementById("editor-container");
var nodeIdIncrement = 0;
var htmlBefore = divContainer.innerHTML; 
var html;
var editedCharIndex;

moveCursorOnDiv("0");

divContainer.addEventListener("input", function(e) {
    html=divContainer.innerHTML;

    editedCharIndex=findFirstDiffPos(htmlBefore,html);
    console.log("html[editedCharIndex]: "+html[editedCharIndex]);

    if(html[editedCharIndex]=="."){
        nodeIdIncrement++; 
      htmlBefore=html.substring(0, editedCharIndex+1)+'</mytag><mytag id="'+nodeIdIncrement+'" contenteditable="true" style="margin: 10px;">'+html.substring(editedCharIndex+1);

      divContainer.innerHTML = htmlBefore;
      moveCursorOnDiv(nodeIdIncrement);
    }else{
        htmlBefore = divContainer.innerHTML;
    }

}, false);

// Find index of newly added character making a diff between previuos situation and present one
function findFirstDiffPos(a, b) {
   var shorterLength = Math.min(a.length, b.length);

   for (var i = 0; i < shorterLength; i++){
       if (a[i] !== b[i]) return i;
   }

   if (a.length !== b.length) return shorterLength;

   return -1;
}

function moveCursorOnDiv(divId){
    console.log("divId: "+divId);
  var el = document.getElementById(divId);
  console.log("inner: "+el.innerHTML);
  var range = document.createRange();
  var sel = window.getSelection();
  range.setStart(el, 0);
  range.collapse(true);
  sel.removeAllRanges();
  sel.addRange(range);
}
mytag {
  border: 1px solid black;
}
<div id='editor-container'>
    <mytag id="0" contenteditable="true">test</mytag>
</div>

In JS Made changes here:

htmlBefore=html.substring(0, editedCharIndex+1)+'</mytag><mytag id="'+nodeIdIncrement+'" contenteditable="true" style="margin: 10px; border: 1px solid black;">'+html.substring(editedCharIndex+1);
like image 118
Achint Avatar answered Nov 16 '22 22:11

Achint