3

I have the following function to add a span tag with selected text.

 function Add() {
    var selObj = window.getSelection();
    var selRange = selObj.getRangeAt(0);
    var newElement = document.createElement("span");
    newElement.setAttribute("class", "cls1");
    var documentFragment = selRange.extractContents();
    newElement.appendChild(documentFragment);
    selRange.insertNode(newElement);
    selObj.removeAllRanges();
}

It works fine. But I want to prevent adding a new span inside another one.

    <a href="#" onclick="Add()">Add Span</a>
    <div id="content">
        Lorem ipsum dolor sit amet, <span class="cls1">consectetur adipisicing</span> elit,
    </div>

If the user selects "adipisicing elit" I don't want to create a new span as "adipisicing" is inside another one. So how can I know whether the selected text includes any part of other span?

Thanks.

Blazemonger
  • 90,923
  • 26
  • 142
  • 180
aliassce
  • 1,197
  • 6
  • 19

3 Answers3

3

Here's one approach, that tests if the current selection is directly part of a <span class="cls1">. You can add it after the first line of your function, since it uses selObj as a starting point.

if ("cls1"!==selObj.anchorNode.parentNode.className) {

If it has to be a span:

if ("span"!==selObj.anchorNode.parentNode.tagName.toLowerCase()) {

To make sure BOTH ends of the selection are outside a span, use focusNode as well:

if ("span"!==selObj.anchorNode.parentNode.tagName.toLowerCase() &&
   ("span"!==selObj.focusNode.parentNode.tagName.toLowerCase()) {

Combining everything:

if ("cls1"!==selObj.anchorNode.parentNode.className &&
    "span"!==selObj.anchorNode.parentNode.tagName.toLowerCase() && 
    "cls1"!==selObj.focusNode.parentNode.className &&
    "span"!==selObj.focusNode.parentNode.tagName.toLowerCase()) {

http://jsfiddle.net/mblase75/Zg5LW/

https://developer.mozilla.org/en-US/docs/DOM/Selection/anchorNode

https://developer.mozilla.org/en-US/docs/DOM/Selection/focusNode

Blazemonger
  • 90,923
  • 26
  • 142
  • 180
  • That doesn't solve the problem. It allows adding span inside another. (If I select some text and some part of blue text it creates a red one -nested span- in your example) – aliassce Apr 23 '13 at 14:08
  • Thans, that seems working. One more question: as I only care the class of the spans I can delete the tagName comparison, right? – aliassce Apr 23 '13 at 14:33
  • On the other hand, If a span is inside the selection from both sides (neither anchornode nor focusnode) what can we do? (selecting "sit" to "elit" in your sample) – aliassce Apr 23 '13 at 14:36
  • Well, you didn't ask that. :-) But you might check that AFTER you've added the new span -- check if any (direct) childNodes have the specified className, and strip the span if they do by replacing the node with its innerHTML. – Blazemonger Apr 23 '13 at 14:38
0

At last I figured out how to achieve this. To sum up I need to check :
1. Whether selection is inside a specific class ("cls1") element.
2. Whether selection contains one or more specific class ("cls1") element.
3. Whether selection contains some part of a specific class ("cls1") element.

Thanks to @Blazemonger's code I could achieve 1. and 3. items:

  if ("cls1" == selObj.anchorNode.parentNode.className || "cls1" ==                selObj.focusNode.parentNode.className)
            { return true; }

And two I added the following part which checks for 2. and 3. items:

              var docFrag = selRange.cloneContents();
              var childNodes = docFrag.childNodes;
              for (var i = 0; i < childNodes.length; i++) {
                 var nodee = childNodes[i];
                 if ($(nodee).hasClass("cls1") || $(nodee).find(".cls1").length > 0)
                  {  return true; }
}

So I wrote a checking function combining the two:

function ContainsDiv(selRange)
{

           var docFrag = selRange.cloneContents();
           var childNodes = docFrag.childNodes;
           for (var i = 0; i < childNodes.length; i++) {
              var nodee = childNodes[i];
              if ($(nodee).hasClass("cls1") || $(nodee).find(".cls1").length > 0)
                {  return true; }

              if ("cls1" == selObj.anchorNode.parentNode.className || "cls1" ==                selObj.focusNode.parentNode.className)
                { return true; }

}

And I used this function inside Add() :

function Add() {
                var selObj = window.getSelection();
                var selRng = selObj.getRangeAt(0);
                if (ContainsDiv(selRng)) {
                    return;
                }
                var newElement = document.createElement("span");
                newElement.setAttribute("class", "cls1");
                var documentFragment = selRng.extractContents();
                newElement.appendChild(documentFragment);
                selRng.insertNode(newElement);
                selObj.removeAllRanges();
            }
aliassce
  • 1,197
  • 6
  • 19
-1
var parentNode = selObj.parentNode;
if (parentNode.nodeName == "SPAN")
{
  //don't add
}
Andrew Walters
  • 4,763
  • 6
  • 35
  • 49