обработка пересечения между отметками выбора

9

У меня есть кнопка пометки на пользовательском интерфейсе, нажав на которую, любой выбранный пользователем отмечен красным. Здесь нет проблем. Я достигаю этогоdocument.execCommand("insertHTML")

Но у меня есть дополнительное требование, что если создается новый выбор, который является пересечением старых отметок выбора, красная отметка старого выделения должна исчезнуть.

Например:

На следующем рисунке: это и тестирование помечены. Теперь, если я выберу его тесты в начале и отметку клика, старые отметки этого и тестирование должны исчезнуть, и только его тесты должны быть отмечены, потому что есть пересечение.

введите описание изображения здесь

код:

const button = document.getElementById("button");

button.addEventListener('click', ()=>{
	const s = window.getSelection();
  const selectionStr = s.toString();
  document.execCommand("insertHTML", false, `<span class="bg-red">${selectionStr}<span>`);
})
.bg-red {  
background: red;
}
<div contenteditable="true">
 this is testing  this is testing  this is testing
</div>

<button id="button">mark</button>

Asdasd
источник

Ответы:

4

Вы можете найти startContainer& endContainerвыбранного текста с getRangeAtи сравнить их с contentContainer. И если они не равны contentContainer, то удалите bg-redкласс.

const button = document.getElementById("button");

button.addEventListener('click', ()=>{
  let contentContainer = document.getElementById("contentContainer");
  const selection = window.getSelection();
  if (selection.rangeCount > 0){
    let startContainer =  selection.getRangeAt(0).startContainer.parentNode;
    let endContainer =  selection.getRangeAt(0).endContainer.parentNode;
    if(startContainer != contentContainer)
      startContainer.classList.remove('bg-red')
    if(endContainer != contentContainer)
      endContainer.classList.remove('bg-red')
  }
  const selectionStr = selection.toString();
  document.execCommand("insertHTML", false, `<span class="bg-red">${selectionStr}<span>`); 
})
.bg-red {  
  background: red;
}
<div id="contentContainer" contenteditable="true">
 this is testing  this is testing  this is testing
</div>

<button id="button">mark</button>

Бахадор Рагибизаде
источник
4

Если вам не нужна поддержка IE, вы можете использовать selection.containsNode : https://developer.mozilla.org/en-US/docs/Web/API/Selection/containsNode

Это позволяет пометить узлы, которые содержатся в выделении. Вам нужно установить для флага partContainment значение true, чтобы он обнаруживал узлы, выбранные только частично.

Таким образом, в первый раз вы помечаете узлы, содержащиеся в выделении, определенным именем класса. Затем вы выполняете execCommand, чтобы применить свой стиль. Затем вы удалили теги, которые были помечены ранее, установив externalHTML на этих узлах в innerHTML . Вы сохраните стиль, который вы только что применили, но удалите предыдущие. Нравится:

const button = document.getElementById("button");

button.addEventListener('click', () => {
  const s = window.getSelection();
  const selectionStr = s.toString();

  tagIntersection(s)
  document.execCommand("insertHTML", false, `<span class="bg-red">${selectionStr}<span>`);
  removeIntersection(s)
})

function tagIntersection(s) {
  const redSpans = document.getElementsByClassName('bg-red')
  for (let i = 0; i < redSpans.length; i++) {
    if (s.containsNode(redSpans[i], true)) {
      redSpans[i].classList.add('to-remove');
    }
  }
}

function removeIntersection(s) {
  // using querySelector because getElements returns a live nodelist 
  // which is a problem when you manipulate the nodes
  const toRemovespans = document.querySelectorAll('.to-remove')
  for (let i = 0; i < toRemovespans.length; i++) {
    toRemovespans[i].outerHTML = toRemovespans[i].innerHTML;
  }
}
.bg-red {
  background: red;
}
<div contenteditable="true" id="editableDiv">
  this is testing this is testing this is testing
</div>

<button id="button">mark</button>

Жюльен Грегуар
источник
0

Я попытался определить, есть ли в getSelection.anchorNode.parentNode класс bg-red, и заменить его, но появляется слишком много побочных эффектов.

Итак, решение с помощью регулярного выражения ...

(* 1) - Если вы проверите код редактируемого содержимого элемента, вы увидите его структуру: каждая новая строка представляет собой отдельный div. Вы должны заменить каждый символ новой строки \nHTML-кодом "разрыва строки", чтобы разрешить многострочный выбор.

(* 2) - Браузер вставляет свои собственные исправления в HTML и предотвращает правильные замены. Я был вынужден вставить любую не-HTML строку, которая вряд ли появится в тексте. И после всех замен - вставьте действительный HTML.

(* 3) - Я рекомендую проанализировать все регулярные выражения здесь → https://regex101.com/r/j88wc0/1 Основная идея - заменить все двойные появления <span class="bg-red"> anything instead of closing span <span class="bg-red">или </span> anything instead of starting span tag </span>.

Обратите внимание, что этот код обновляет все innerHTML каждый раз, когда вы нажимаете кнопку. Это не будет работать так же хорошо с текстом из нескольких тысяч строк)

let button = document.getElementById("button");
let block = document.getElementById("block");

button.addEventListener('click', function(){
  let s = window.getSelection();
  let str = s.toString().replace(/\n/g,'</span></div><div><span class="bg-red">'); // (*1)

  document.execCommand("insertHTML", false, `bubufication`); // (*2)

  block.innerHTML = block.innerHTML
    .replace(/bubufication/, `<span class="bg-red">${str}</span>`)
    .replace(/<span class="bg-red">(.*?)<\/span><span class="bg-red">/g,'$1<span class="bg-red">')
    .replace(/<span class="bg-red">(((?!<\/span>).)*?<span class="bg-red">)/g,"$1")
    .replace(/(<\/span>((?!<span class="bg-red">).)*)<\/span>/g,"$1"); // (*3)
});
.bg-red {
  background-color: red;
}
<div id="block" contenteditable="true">
  <div>this is testing  this is testing  this is testing</div>
  <div>this is testing  this is testing  this is testing</div>
</div>

<button id="button">mark</button>

ОПТИМУС ПРАЙМ
источник