gestion de l'intersection entre les marques de sélection

9

J'ai un bouton de marque sur l'interface utilisateur, en cliquant sur lequel, toute sélection d'utilisateur est marquée en rouge. Aucun problème ici. J'y parviens endocument.execCommand("insertHTML")

Mais j'ai une exigence supplémentaire que si la nouvelle sélection est créée qui est l'intersection des anciennes marques de sélections, la marque rouge de l'ancienne sélection devrait disparaître.

Par exemple:

Dans l'image suivante: ceci et testing sont marqués. Maintenant, si je sélectionne ses is tes depuis le début et que je clique sur la marque, les anciennes marques de ceci et les tests devraient disparaître et seuls ses is tes devraient être marqués car il y a une intersection.

entrez la description de l'image ici

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
la source

Réponses:

4

Vous pouvez trouver le startContainer& endContainerdu texte sélectionné avec getRangeAtet les comparer avec contentContainer. Et s'ils ne sont pas égaux à contentContainer, supprimez 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>

Bahador Raghibizadeh
la source
4

Si vous n'avez pas besoin de support IE, vous pouvez utiliser selection.containsNode : https://developer.mozilla.org/en-US/docs/Web/API/Selection/containsNode

Cela vous permet de marquer les nœuds contenus dans la sélection. Vous devez définir l' indicateur partialContainment sur true, afin qu'il détecte les nœuds qui ne sont que partiellement sélectionnés.

Ainsi, dans un premier temps, vous marquez les nœuds contenus par sélection avec un nom de classe spécifique. Ensuite, vous faites votre execCommand pour appliquer votre style. Ensuite, vous avez supprimé les balises marquées précédemment en définissant externalHTML sur ces nœuds sur innerHTML . Vous conserverez le style que vous venez d'appliquer, mais supprimerez les précédents. Comme ça:

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
la source
0

J'ai essayé de détecter si 'getSelection'.anchorNode.parentNode a la classe bg-red et de la remplacer, mais trop d'effets secondaires apparaissent.

Donc, la solution avec regexp ...

(* 1) - Si vous inspectez le code de l'élément modifiable par le contenu, vous verrez sa structure: chaque nouvelle ligne est une div séparée. Vous devez remplacer chaque symbole de nouvelle ligne \npar du HTML «saut de ligne» pour permettre les sélections sur plusieurs lignes.

(* 2) - Le navigateur insère ses propres corrections au format HTML et empêche les remplacements corrects. J'ai été obligé d'insérer une chaîne non HTML, qui ne devrait pas apparaître dans le texte. Et après tous les remplacements - insérez du code HTML valide.

(* 3) - Je recommande d'analyser toutes les expressions régulières ici → https://regex101.com/r/j88wc0/1 L'idée principale est de remplacer toutes les doubles apparitions de <span class="bg-red"> anything instead of closing span <span class="bg-red">ou </span> anything instead of starting span tag </span>.

Notez que ce code rafraîchit tous les innerHTML, chaque fois que vous appuyez sur le bouton. Cela ne fonctionnera pas aussi bien avec du texte de quelques milliers de lignes)

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
la source