Range.setStart using only character offset?

I am completely new to Javascript and I have been reading the documentation for the last couple of days trying to figure this out. I finally had to resort to portraying my ignorance here.

I have an integer that is the index of a character in a paragraph. I want to find the bounding rectangle of this character. I am trying to do this by creating a range that contains a character.

So, I tried:

var range = document.createRange();
range.setStart (node, offsetInsideNode);

      

and for node I tried to pass a paragraph element and for offsetInsideNode I passed an integer characterOffset.

But then I discovered, "If a node element can have child nodes, the offsetInsideNode parameter specifies the position of the child node in the childNodes collection of the node element, otherwise it sets the character position in the text content of the node element.

But I want to ONLY use the character position. And I can't figure out how to do this as it only seems to want to use the child position of the node. I guess I am missing something.

Let's say I have a paragraph:

<p xmlns="http://www.w3.org/1999/xhtml" class="s12" style="color: rgb(0, 0, 0);"><span class="mySpanClass">The</span> quick brown <b>fox</b> jumps over the lazy dog</p>

      

And I want to find the bounding rectangle of the nth character, how would I do that? I am barking the wrong tree, and is there a much easier way I have left out?

Thank.

notes:

  • javascript only
  • no libraries
  • no jquery
  • for UIWebview on iOS device
+3


source to share


2 answers


Hope this is what you are looking for. This function basically takes the content of the element you want to find nth in, breaks it down into characters, finds it nth

without regard to HTML tags, wraps it nth

in a time slot and reads offsetTop

and offsetLeft

replaces it with the original content. Then the x and y offset is returned as an object.

function nthCharOffset(nth, element){
    var orgContent = element.innerHTML;   // Save the original content.
    var arr = orgContent.split('');         // Split every character.

    // Few vars to control the upcoming loop
    var content = '';
    var tag = false;
    var count = 0;

    // Loop through every character creating a new string and wrapping the nth in a temporary span
    for (var i = 0; i < arr.length; i++) {

        // if inside tag, don't count this in the nth count
        if (arr[i] == '<') tag = true
        if (!tag) count++;
        if (arr[i] == '>') tag = false;

        // If this charactar is nth, wrap it in a temporary span
        if (nth == count) content += '<span id="offset-check">' + arr[i] + '</span>';
        else content += arr[i];
    }

    // Set the content with the temporary span.
    element.innerHTML = content;

    // Get the offset of the temporary span.
    var offsetCheck = document.getElementById('offset-check');
    var offset = {x: offsetCheck.offsetLeft , y: offsetCheck.offsetTop }

    // Remove the span.
    element.innerHTML = orgContent;

    // Return the result.
    return offset;
}

      

Use it like this:



nthCharOffset(10, document.getElementById('element'));

      

I made a fiddle so you can test it here .

This script uses this function to position and scale the red rectangle to fit the symbol nth

.

+2


source


As I understand it you want

ONLY symbol position

to specify a range to select, not some node-offset. I believe I had the same problem. An offset can be created by addressing nodes or characters. The method Range.setStart

does two things at the same time, it's a pain, but the documentation gives you what you need to do:

If startNode is a Node of type Text, Comment, or CDATASection, then startOffset is the number of characters from the beginning of startNode.

See: https://developer.mozilla.org/en-US/docs/Web/API/Range/setStart

Therefore, you can specify a range with a character index by referring to the element's Node text. Here is the (optimistic) solution that I came up with.



Let's say you have a document and want to add sticky CSS styled yellow lights to a piece of selected text. In Javascript create a range and select an element. Add a range, but select the text index first. The offset then refers to characters, not nodes.

// Magic happens here
function getTextNodeFrom(element) {

  // replace with more sophisticated method
  return element.childNodes[0];

}

// Create the range as you normally would
function createRange(node, start, end) {
  var range = document.createRange();
  range.setStart(node, start);
  range.setEnd(node, end);
  return range;
}

// Give it a nice name 
function createRangeByCharacterOffset(element, start, end) {

  // Rather than passing the element directly to createRange,  
  // extract the node first.
  var textNode = getTextNodeFrom(element)
  var range = createRange(textNode, start, end);

  return range;
}

// To illustrate, let surround the range with a highlighting span
function highlight () {
    var span = document.createElement('span');
    span.setAttribute('class', 'highlight');
    return span;
}

var p = document.getElementsByTagName('p')[0];

createRangeByCharacterOffset(p, 50, 200)
.surroundContents(highlight()); // This performs the actual highlight. 

      

This item is selected first p

. Then we retrieve the textNode using a function getTextNodeFrom(element)

. Please note that this feature is really optimistic. It assumes that the list of nodes contains a textNode as the first element, which does not have to occur. How you get this Node is up to you, and only a simple approach is shown here. I would suggest that iterating over the list and checking node types for text or CDATA would be sufficient.

The range is created and the start and end are set. When node 'object is text, offset refers to characters. Take a look at this violin to see it in action.

As an example, the code shown takes a selected range and surrounds it with a styled span element to highlight the range from character 50

to 200

.

https://jsfiddle.net/cessor/t6sroob1/2/

0


source







All Articles