ryuutatsuo
ryuutatsuo

Reputation: 3996

Get elements index with in text block

Is it possible to get a tags position within a text block. For example I have a huge p tag and within it is a bunch of text. The user will have a tool which will dynamically insert a bunch of span tags into the p tag. At one point the user will be finished and I want to save what they have done. Due to limitation I am unable to just save the entire contents of the p tag instead I have to get each individual span.

Initial text

<p>Sam wanted a dog.
   "If you're a good boy," said his father.
   "When you can take care of it yourself" said his mother.
   Sam cleaned up his room. He ate carrots and broccoli. He stopped making monster noises
   at night to scare Molly, his older sister. He hung up his cap after baseball practice.
</p>

After user interaction

<p>Sam wanted a dog.
   "If you're <span>a good boy,"</span> said his father.
   "When you can take care of it yourself" said his mother.
   Sam cleaned up his <span>room. He ate</span> carrots and broccoli. He stopped making monster noises
   at night to scare Molly, his older sister. He hung up his cap after baseball practice.
</p>

I guess what I am looking for is a range where does the span start and where does it end. All I have been able to do so far is just loop through the content but I am stuck at finding out where to go from there. Reason why I need to save is because user is expecting to return to their content the way they left it. So the solution will need to consider putting back the span tags where they have been taken from.

Sample JS of how I would start of

$("p").each(function (index) {
     $(this).find("span").each(function () {
           console.log(this);
     });
});

My real environment is more complex but I have simplified it to the basics to narrow the solutions down. Any help/suggestions is greatly appreciated.

Upvotes: 0

Views: 112

Answers (2)

acdcjunior
acdcjunior

Reputation: 135762

Here's a function that will take into account where the spans start and end. Using pure JavaScript.

function getSpanRanges(myP) {
    var start = -1, result = [], parts = [], partsTypes = [];
    for (var i = 0; i < myP.childNodes.length; i++) {
        parts[i] = myP.childNodes[i].outerHTML || myP.childNodes[i].nodeValue;
        partsTypes[i] = myP.childNodes[i].nodeName;
        if ("SPAN" == myP.childNodes[i].nodeName) { result.push([start + 1, start + parts[i].length]); }
        start += parts[i].length;
    }
    return result;
}

Example usage:

var myP = document.getElementsByTagName("p")[0];
var spanRanges = getSpanRanges(myP); // this is the ranges array

See EXAMPLE DEMO here.

Since you need a solution that will need to consider putting back the span tags where they have been taken from, the function above has three possible outputs:

  • Array of the elements:

    ["Sam wanted a dog. \"If you're ", "<span>a good boy,\"</span>", " said his father. \"When you can take care of it yourself\" said his mother. Sam cleaned up his ", "<span>room. He ate</span>", " carrots and broccoli. He stopped making monster n…ster. He hung up his cap after baseball practice."]
    
  • Array of their types:

    ["#text", "SPAN", "#text", "SPAN", "#text"]
    
  • An array with their ranges (starting, end):

    [[29, 53], [148, 172]]
    

Upvotes: 2

Bergi
Bergi

Reputation: 664425

Use the .contents method to get all child nodes of the paragraph, including the text nodes. Now you can easily loop over them:

var ranges = [],
    i = 0;
$("thatp").contents().each(function() {
    var $this = $(this);
    if (this.nodeType == 1 && $this.is("span"))
        ranges.push([i, i+=$this.text().length]);
    else
        i+=$this.text().length;
});
// result:
> ranges
[[31,43],[141,153]] // at least in my console test, you might have different whitespaces

Upvotes: 2

Related Questions