manejo de intersección entre marcas de selección

9

Tengo un botón de marca en la interfaz de usuario, haciendo clic en el cual, cualquier selección de usuario se marca en rojo. No hay problemas aquí. Lo logro pordocument.execCommand("insertHTML")

Pero tengo un requisito adicional de que si se crea la nueva selección, que es la intersección de las marcas de las selecciones antiguas, la marca roja de la selección anterior debería desaparecer.

Como ejemplo:

En la siguiente imagen: esta y las pruebas están marcadas. Ahora, si selecciono sus pruebas desde el principio y hago clic en la marca, las marcas antiguas de esto y las pruebas deberían desaparecer y solo su prueba debería marcarse porque hay una intersección.

ingrese la descripción de la imagen aquí

código:

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
fuente

Respuestas:

4

Puede encontrar el startContainer& endContainerdel texto seleccionado con getRangeAty compararlos con contentContainer. Y si no son iguales contentContainer, elimine la bg-redclase.

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
fuente
4

Si no necesita soporte de IE, puede usar selection.containsNode : https://developer.mozilla.org/en-US/docs/Web/API/Selection/containsNode

Esto le permite marcar los nodos contenidos en la selección. Debe establecer el indicador partialContainment en true, por lo que detectará nodos que solo estén parcialmente seleccionados.

Entonces, por primera vez, marca los nodos contenidos en la selección con un nombre de clase específico. Luego haces tu execCommand para aplicar tu estilo. Luego, eliminó las etiquetas que se marcaron anteriormente al establecer externalHTML en estos nodos en innerHTML . Mantendrá el estilo que acaba de aplicar, pero eliminará los anteriores. Me gusta esto:

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
fuente
0

He intentado detectar si 'getSelection'.anchorNode.parentNode tiene la clase bg-red, y reemplazarlo, pero aparecen demasiados efectos secundarios.

Entonces, la solución con regexp ...

(* 1) - Si inspecciona el código del elemento editable por contenido, verá su estructura: cada nueva línea es un div separado. Debe reemplazar cada símbolo de nueva línea \ncon HTML "salto de línea", para permitir selecciones de varias líneas.

(* 2) : el navegador inserta sus propias correcciones en HTML y evita los reemplazos correctos. Me obligaron a insertar cualquier cadena que no sea HTML, que probablemente no aparezca en el texto. Y después de todos los reemplazos, inserte HTML válido.

(* 3) - Recomiendo analizar todas las expresiones regulares aquí → https://regex101.com/r/j88wc0/1 La idea principal es reemplazar todas las apariencias dobles de <span class="bg-red"> anything instead of closing span <span class="bg-red">o </span> anything instead of starting span tag </span>.

Tenga en cuenta que este código actualiza todo innerHTML cada vez que presiona el botón. No funcionará tan bien con texto de unos pocos miles de líneas)

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
fuente