Umgang mit Schnittpunkten zwischen Auswahlmarkierungen

9

Ich habe eine Markierungsschaltfläche auf der Benutzeroberfläche, auf die geklickt wird. Jede Benutzerauswahl wird rot markiert. Keine Probleme hier. Ich erreiche das durchdocument.execCommand("insertHTML")

Ich habe jedoch eine zusätzliche Anforderung, dass die rote Markierung der alten Auswahl verschwinden sollte, wenn die neue Auswahl erstellt wird, die den Schnittpunkt der alten Auswahlmarkierungen darstellt.

Als Beispiel:

Im folgenden Bild: Dies und Testen sind markiert. Nun , wenn ich wähle sein tes ist von Anfang an und Markierung klicken, alte Markierungen von diesem und Tests sollen weggehen und nur sein tes markiert werden soll , weil es ein Schnittpunkt ist.

Geben Sie hier die Bildbeschreibung ein

Code:

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
quelle

Antworten:

4

Sie können das startContainer& endContainerdes ausgewählten Textes mit getRangeAtfinden und mit vergleichen contentContainer. Und wenn sie nicht gleich sind contentContainer, entfernen Sie die bg-redKlasse.

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>

Bahador Raghibizadeh
quelle
4

Wenn Sie keine IE-Unterstützung benötigen, können Sie selection.containsNode verwenden : https://developer.mozilla.org/en-US/docs/Web/API/Selection/containsNode

Auf diese Weise können Sie die Knoten markieren, die in der Auswahl enthalten sind. Sie müssen das Flag teilweiseContainment auf true setzen, damit Knoten erkannt werden, die nur teilweise ausgewählt sind.

Sie kennzeichnen also zum ersten Mal die Knoten, die in der Auswahl enthalten sind, mit einem bestimmten Klassennamen. Dann führen Sie Ihren execCommand aus , um Ihren Stil anzuwenden. Anschließend haben Sie die zuvor gekennzeichneten Tags gelöscht, indem Sie OuterHTML auf diesen Knoten auf InnerHTML gesetzt haben . Sie behalten den gerade angewendeten Stil bei, entfernen jedoch die vorherigen. So was:

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>

Julien Grégoire
quelle
0

Ich habe versucht festzustellen, ob 'getSelection'.anchorNode.parentNode die Klasse bg-red hat, und sie ersetzt, aber es treten zu viele Nebenwirkungen auf.

Also, die Lösung mit Regexp ...

(* 1) - Wenn Sie den Code des inhaltsbearbeitbaren Elements überprüfen, sehen Sie dessen Struktur: Jede neue Zeile ist ein separates Div. Sie müssen jedes neue \nZeilensymbol durch HTML mit "Zeilenumbruch" ersetzen , um eine mehrzeilige Auswahl zu ermöglichen.

(* 2) - Der Browser fügt seine eigenen Korrekturen in HTML ein und verhindert korrekte Ersetzungen. Ich musste eine Nicht-HTML-Zeichenfolge einfügen, die wahrscheinlich nicht im Text erscheint. Und nach allen Ersetzungen - fügen Sie gültiges HTML ein.

(* 3) - Ich empfehle, alle regulären Ausdrücke hier zu analysieren → https://regex101.com/r/j88wc0/1 Die Hauptidee besteht darin, alle doppelten Erscheinungen von <span class="bg-red"> anything instead of closing span <span class="bg-red">oder zu ersetzen </span> anything instead of starting span tag </span>.

Beachten Sie, dass dieser Code jedes Mal, wenn Sie auf die Schaltfläche klicken, das gesamte innerHTML aktualisiert. Es funktioniert nicht so gut mit Text aus einigen tausend Zeilen.

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>

OPTIMUS PRIME
quelle