поводження з перетином між позначеннями вибору


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>

Відповіді:


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

Це дозволяє позначити вузли, які містяться у виділенні. Потрібно встановити прапор parcialContainment на істинне, тож він виявить вузли, вибрані лише частково.

Отже, ви вперше позначите вузли, які містяться шляхом вибору із конкретною назвою класу. Тоді ви виконайте свій execCommand, щоб застосувати свій стиль. Потім ви видалили теги , які були помічені раніше, встановивши outerHTML на цих вузлах в 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-червоний, і замінити його, але з’являється занадто багато побічних ефектів.

Отже, рішення з regexp ...

(* 1) - Якщо ви перевірите код елемента, який можна редагувати вміст, ви побачите його структуру: Кожен новий рядок є окремим подільником. Ви повинні замінити кожен символ нового рядка \nна "переривчастий" HTML, щоб дозволити багаторядковий вибір.

(* 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>.

Зауважте, що цей код оновлює всі внутрішні HTMLML щоразу, коли ви натискаєте кнопку. Це не буде добре працювати з текстом з кількох тисяч рядків)

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>

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.