Great Day Today Great Day Today - 3 months ago 8
Javascript Question

Prevent span being inside another span with insertNode

I have this code. On select of text, a div that contains colors will appear. On click of one of the colors, the selected text will contain that highlight. However, how can I check if the highlight is inside another highlight? If the highlight is inside another highlight, I want an alert to appear that this action isn't possible. Otherwise, the highlight will be created.

NOTE: You can completely ignore the html and css section.

This is an example of a highlight within a highlight. I highlighted the phrase

There Hello There Hello There Hello
with yellow. Then, I highlighted inside the yellow highlighter
e Hello There There Hello
.

enter image description here



$("#tooltip").mousedown(function(event) {
event.preventDefault();
});

//Only add the listener once, not another listener each mouseup
$(".boxes").click(function() {
var selection = document.getSelection();
var range = selection.getRangeAt(0);
var contents = range.extractContents();
var node = document.createElement("span");
node.classList.add($(this).attr("data-mark"));
node.appendChild(contents);
// Go ahead and create the span
range.insertNode(node);
selection.removeAllRanges(); //Clear the selection, showing highlight
hideTooltip();
});
function hideTooltip() {
document.getElementById('tooltip').style.display = ''; //hide the tooltip
}
$("#actual_verse").mouseup(function() {
var text = "";
if (window.getSelection) {
text = window.getSelection().toString();
} else if (document.selection && document.selection.type != "Control") {
text = document.selection.createRange().text;
}
if (/\S/.test(text)) {
// Tool Tip
var ele = document.getElementById('tooltip');
var sel = window.getSelection();
var rel1 = document.createRange();
rel1.selectNode(document.getElementById('cal1'));
var rel2 = document.createRange();
rel2.selectNode(document.getElementById('cal2'));
if (!sel.isCollapsed) {
var r = sel.getRangeAt(0).getBoundingClientRect();
var rb1 = rel1.getBoundingClientRect();
var rb2 = rel2.getBoundingClientRect();
//this will place ele below the selection
ele.style.top = (r.bottom - rb2.top) * 100 / (rb1.top - rb2.top) + 'px';
//this will align the right edges together
ele.style.left = (r.left - rb2.left) * 100 / (rb1.left - rb2.left) + 'px';
//code to set content
ele.style.display = 'block';
}
// End of Tool Tip
}
});

/* Highlighting */
.blue_mark {
background: #AAF6FF;
cursor: pointer;
}
.red_mark {
background: #FF9B9F;
cursor: pointer;
}
.green_mark {
background: #D6FFAA;
cursor: pointer;
}
.yellow_mark {
background: #FFF8AA;
cursor: pointer;
}
.orange_mark {
background: #FFBF98;
cursor: pointer;
}
.purple_mark {
background: #D7D5FC;
cursor: pointer;
}
/* End of Highlighting */

/* Tool Kit */
#tooltip {
position: absolute;
display: none;
border: grey solid 1px;
background: #373737;
padding: 5px;
border-radius: 3px;
}
#cal1 {
position: absolute;
height: 0px;
width: 0px;
top: 100px;
left: 100px;
overflow: none;
z-index: -100;
}
#cal2 {
position: absolute;
height: 0px;
width: 0px;
top: 0;
left: 0;
overflow: none;
z-index: -100;
}
.boxes {
width: 15px;
height: 15px;
cursor: pointer;
display: inline-block;
margin-right: 2px;
position: relative;
top: 3px;
}
#blue_box {
background: #AAF6FF;
}
#green_box {
background: #D6FFAA;
}
#orange_box {
background: #FFBF98;
}
#purple_box {
background: #D7D5FC;
}
#red_box {
background: #FF9B9F;
}
#yellow_box {
background: #FFF8AA;
}
.highlight {
background: yellow;
}
/* End of Tool Kit */

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span id='actual_verse' class='context'>Hello There Hello There Hello There Hello There! Hello There</span>
<div id='cal1'>&nbsp;</div>
<div id='cal2'>&nbsp;</div>
<div id='tooltip'>
<div id='blue_box' class='boxes' title='Blue' data-mark='blue_mark'></div>
<div id='green_box' class='boxes' title='Green' data-mark='green_mark'></div>
<div id='yellow_box' class='boxes' title='Yellow' data-mark='yellow_mark'></div>
<div id='orange_box' class='boxes' title='Orange' data-mark='orange_mark'></div>
<div id='purple_box' class='boxes' title='Purple' data-mark='purple_mark'></div>
<div id='red_box' class='boxes' title='Red' data-mark='red_mark'></div>
</div>




Answer

You can force the select to start and end in the same node, and check the parent:

var verse = document.getElementById("actual_verse");
if(range.startContainer !== range.endContainer ||
   range.startContainer.parentElement !== verse ||
   range.endContainer.parentElement !== verse
) {
  alert("Not possible");
  return;
}

var verse = document.getElementById("actual_verse");

$("#tooltip").mousedown(function(event) {
  event.preventDefault();
});

//Only add the listener once, not another listener each mouseup
$(".boxes").click(function() {
  var selection = document.getSelection();
  var range = selection.getRangeAt(0);
  if(range.startContainer !== range.endContainer ||
     range.startContainer.parentElement !== verse ||
     range.endContainer.parentElement !== verse
  ) {
    alert("Not possible");
    return;
  }
  var contents = range.extractContents();
  var node = document.createElement("span");
  node.classList.add($(this).attr("data-mark"));
  node.appendChild(contents);
  // Go ahead and create the span
  range.insertNode(node);
  selection.removeAllRanges(); //Clear the selection, showing highlight
  hideTooltip();
});
function hideTooltip() {
  document.getElementById('tooltip').style.display = ''; //hide the tooltip
}
$("#actual_verse").mouseup(function() {
  var text = "";
  if (window.getSelection) {
    text = window.getSelection().toString();
  } else if (document.selection && document.selection.type != "Control") {
    text = document.selection.createRange().text;
  }
  if (/\S/.test(text)) {
    // Tool Tip
    var ele = document.getElementById('tooltip');
    var sel = window.getSelection();
    var rel1 = document.createRange();
    rel1.selectNode(document.getElementById('cal1'));
    var rel2 = document.createRange();
    rel2.selectNode(document.getElementById('cal2'));
    if (!sel.isCollapsed) {
      var r = sel.getRangeAt(0).getBoundingClientRect();
      var rb1 = rel1.getBoundingClientRect();
      var rb2 = rel2.getBoundingClientRect();
      //this will place ele below the selection
      ele.style.top = (r.bottom - rb2.top) * 100 / (rb1.top - rb2.top) + 'px';
      //this will align the right edges together
      ele.style.left = (r.left - rb2.left) * 100 / (rb1.left - rb2.left) + 'px';
      //code to set content
      ele.style.display = 'block';
    }
    // End of Tool Tip
  }
});
/* Highlighting */
.blue_mark {
  background: #AAF6FF;
  cursor: pointer;
}
.red_mark {
  background: #FF9B9F;
  cursor: pointer;
}
.green_mark {
  background: #D6FFAA;
  cursor: pointer;
}
.yellow_mark {
  background: #FFF8AA;
  cursor: pointer;
}
.orange_mark {
  background: #FFBF98;
  cursor: pointer;
}
.purple_mark {
  background: #D7D5FC;
  cursor: pointer;
}
/* End of Highlighting */

/* Tool Kit */
#tooltip {
  position: absolute;
  display: none;
  border: grey solid 1px;
  background: #373737;
  padding: 5px;
  border-radius: 3px;
}
#cal1 {
  position: absolute;
  height: 0px;
  width: 0px;
  top: 100px;
  left: 100px;
  overflow: none;
  z-index: -100;
}
#cal2 {
  position: absolute;
  height: 0px;
  width: 0px;
  top: 0;
  left: 0;
  overflow: none;
  z-index: -100;
}
.boxes {
  width: 15px;
  height: 15px;
  cursor: pointer;
  display: inline-block;
  margin-right: 2px;
  position: relative;
  top: 3px;
}
#blue_box {
  background: #AAF6FF;
}
#green_box {
  background: #D6FFAA;
}
#orange_box {
  background: #FFBF98;
}
#purple_box {
  background: #D7D5FC;
}
#red_box {
  background: #FF9B9F;
}
#yellow_box {
  background: #FFF8AA;
}
.highlight {
  background: yellow;
}
/* End of Tool Kit */
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span id='actual_verse' class='context'>Hello There Hello There Hello There Hello There! Hello There</span>
<div id='cal1'>&nbsp;</div>
<div id='cal2'>&nbsp;</div>
<div id='tooltip'>
  <div id='blue_box' class='boxes' title='Blue' data-mark='blue_mark'></div>
  <div id='green_box' class='boxes' title='Green' data-mark='green_mark'></div>
  <div id='yellow_box' class='boxes' title='Yellow' data-mark='yellow_mark'></div>
  <div id='orange_box' class='boxes' title='Orange' data-mark='orange_mark'></div>
  <div id='purple_box' class='boxes' title='Purple' data-mark='purple_mark'></div>
  <div id='red_box' class='boxes' title='Red' data-mark='red_mark'></div>
</div>