Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

handling intersection between selection markings

Tags:

javascript

I have a mark button on UI, clicking which, any user selection is marked red. No problems here. I achieve this by document.execCommand("insertHTML")

But I have an additional requirement that if the new selection is created which is the intersection of old selections markings, old selection's red marking should disappear.

As an example:

In the following image: this and testing are marked. Now if I select his is tes from the start and click mark, old markings of this and testing should go away and only his is tes should be marked because there is an intersection.

enter image description here

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>
like image 837
asdasd Avatar asked Oct 26 '19 10:10

asdasd


2 Answers

You can find the startContainer & endContainer of the selected text with getRangeAt, and compare them with contentContainer. And if they are not equal with contentContainer, then remove the bg-red class.

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>
like image 117
Bahador Raghibizadeh Avatar answered Oct 09 '22 02:10

Bahador Raghibizadeh


If you don't need IE support, you could use selection.containsNode: https://developer.mozilla.org/en-US/docs/Web/API/Selection/containsNode

This allows you to flag the nodes that are contained in the selection. You need to set the partialContainment flag to true, so it'll detect nodes that are only partially selected.

So in a first time you flag the nodes that are contained by selection with a specific class name. Then you do your execCommand to apply your style. Then, you deleted the tags that were flagged previously by setting outerHTML on these nodes to innerHTML. You'll keep the style you just applied, but will remove previous ones. Like this:

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>
like image 30
Julien Grégoire Avatar answered Oct 09 '22 02:10

Julien Grégoire