gestire l'intersezione tra i segni di selezione


9

Ho un pulsante contrassegno sull'interfaccia utente, facendo clic su, ogni selezione dell'utente è contrassegnata in rosso. Nessun problema qui. Lo raggiungo entrodocument.execCommand("insertHTML")

Ma ho un requisito aggiuntivo che se viene creata la nuova selezione che è l'intersezione dei segni di selezione vecchi, il segno rosso di selezione vecchia dovrebbe scomparire.

Come esempio:

Nell'immagine seguente: questo e i test sono contrassegnati. Ora, se seleziono il suo is tes dal segno di inizio e clic, i vecchi segni di questo e il test dovrebbero andare via e solo il suo è tes dovrebbe essere segnato perché c'è un incrocio.

inserisci qui la descrizione dell'immagine

codice:

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>

Risposte:


4

Puoi trovare il startContainer& endContainerdel testo selezionato con getRangeAte confrontarlo con contentContainer. E se non sono uguali contentContainer, quindi rimuovere la bg-redclasse.

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

Se non hai bisogno del supporto IE, puoi utilizzare selection.containsNode : https://developer.mozilla.org/en-US/docs/Web/API/Selection/containsNode

Ciò consente di contrassegnare i nodi contenuti nella selezione. È necessario impostare il flag partialContainment su true, in modo che rilevi nodi selezionati solo parzialmente.

Quindi, per la prima volta, contrassegni i nodi contenuti per selezione con un nome di classe specifico. Quindi esegui il comando execCommand per applicare il tuo stile. Quindi, hai eliminato i tag precedentemente contrassegnati impostando outerHTML su questi nodi su innerHTML . Manterrai lo stile appena applicato, ma rimuoverai quelli precedenti. Come questo:

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

Ho provato a rilevare se 'getSelection'.anchorNode.parentNode ha classe bg-red e la sostituisco, ma compaiono troppi effetti collaterali.

Quindi, la soluzione con regexp ...

(* 1) - Se controlli il codice dell'elemento modificabile del contenuto, vedrai la sua struttura: ogni nuova riga è un div separato. È necessario sostituire ogni simbolo di nuova riga \ncon HTML "interruzione di riga", per consentire selezioni su più righe.

(* 2) - Il browser inserisce le proprie correzioni in HTML e impedisce sostituzioni corrette. Sono stato costretto a inserire qualsiasi stringa non HTML, che non è probabile che appaia nel testo. E dopo tutte le sostituzioni, inserisci un codice HTML valido.

(* 3) - Consiglio di analizzare tutte le espressioni regolari qui → https://regex101.com/r/j88wc0/1 L'idea principale è quella di sostituire tutte le doppie apparizioni di <span class="bg-red"> anything instead of closing span <span class="bg-red">o </span> anything instead of starting span tag </span>.

Si noti che questo codice aggiorna tutto innerHTML, ogni volta che si preme il pulsante. Non funzionerà altrettanto bene con il testo di poche migliaia di righe)

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>

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.