В случае, если два смежных редактируемых диапазона и курсор находятся посередине, как HTML решает, какой из них я пишу? - PullRequest
7 голосов
/ 04 июня 2019

У меня есть этот скрипт, добавляющий новый интервал внутри редактируемого div каждый раз, когда пользователь вводит точку во входном тексте, пытаясь отделить текст, написанный в разных интервалах, на основе наличия разделяющей их точки.(Я использую пользовательский тег 'mytag', но на самом деле он ведет себя как 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);
}

Здесь это JSFIDDLE

Допустим, я ввел символ 'A' в редактируемом div "editor-container", результат будет:

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

Затем я добавлю точку.Результат будет:

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

Когда точка будет добавлена, я программно (функция moveCursorOnDiv) заставляю курсор перемещаться по новому div.Ожидаемым результатом будет то, что ввод другого символа 'B' приведет к ситуации:

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

Вместо этого это фактически приведет к Chrome в:

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

В то время как в Firefox ведет себя как ожидалось90% раз, но иногда случайным образом ведет себя как в Chrome.

Итак, если у меня есть курсор между двумя соседними промежутками, как html решит, будет ли следующий вход помещен в промежуток или в другой?Есть способ вызвать ожидаемое поведение?

РЕДАКТИРОВАТЬ: Совершенно очевидно, что это происходит только в том случае, если следующий div пуст, поскольку есть неоднозначность.Если вы поместите символ в следующий div и переместите каретку в положение «1», она будет работать как шарм.(Просто чтобы сказать, что проблема не зависит напрямую от этого конкретного кода, который в разных условиях работает идеально)

JSFIDDLE 2

Это также означает, что проблема может бытьрешено добавить что-то вроде & nb sp;при добавлении нового div, но это не чистое решение.

Ответы [ 2 ]

1 голос
/ 07 июня 2019

Первый: вы устанавливаете <div id="editor-container".....> </div> как редактируемый, поэтому, когда вы набираете ., он создает новый <mytag> с новым Id, но не дает редактируемую опцию новому <mytag> и, какон недоступен для редактирования, указатель мыши остается в том же положении.

Здесь я внес некоторые изменения: добавил некоторые границы и поля, чтобы он был виден

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>

В JS Произошли изменения здесь:

htmlBefore=html.substring(0, editedCharIndex+1)+'</mytag><mytag id="'+nodeIdIncrement+'" contenteditable="true" style="margin: 10px; border: 1px solid black;">'+html.substring(editedCharIndex+1);
0 голосов
/ 14 июня 2019

Вы должны установить его как редактируемый, поэтому, когда вы печатаете, он создает новый с новым идентификатором, но не дает редактируемому параметру новый и, поскольку он не редактируется, указатель мыши остается в той же позиции.

изменения, которые я сделал: добавлены некоторые границы и поля, чтобы они были видны

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);



<!-- begin snippet: js hide: false console: true babel: false -->
mytag {
  border: 1px solid black;
}

<div id='editor-container'>
    <mytag id="0" contenteditable="true">test</mytag>
</div>
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);
}
...