Firefox triple click selection returns incorrect s

2019-02-20 01:08发布

问题:

I have this piece of code which is supposed to return the start and end offsets of the user selection:

<!DOCTYPE html>
 <html>
  <head>
   <script type="text/javascript">
    function getSelRange() {
      var selObj = window.getSelection();
      var range  = selObj.getRangeAt(0);
      alert(range.startOffset+"-"+range.endOffset);
    }

   </script>
  </head>
  <body>
    <button onclick="getSelRange()">Get the selected text!</button>
    <p>Select some text!</p>
  </body>
</html>

When I select some text from the p by dragging, it alerts the numbers correctly. However, when I select the entire text in the p by triple clicking on it, it alerts 0-1. Looks like in Firefox only, triple click doesn't return the selection range correctly.

How do I get the correct start and end points on triple click as well?

回答1:

Firefox is returning the correct range. The problem is that your assumption that a range must have its start and end boundaries relative to a text node is incorrect.

What is happening is that Firefox is reporting the range as starting before the zeroth child node of the <p> element and ending after the first child of the <p> element. This is perfectly valid.

You could do something like the following to adjust such a range to make its boundaries lie inside the text node within the <p> element:

Demo: http://jsfiddle.net/DbmjH/2/

Code:

function adjustRange(range) {
    range = range.cloneRange();
    if (range.startContainer.nodeType != 3) {
        var nodeAfterStart = range.startContainer.childNodes[range.startOffset];
        if (nodeAfterStart && nodeAfterStart.nodeType == 3) {
            range.setStart(nodeAfterStart, 0);
        }
    }
    if (range.endContainer.nodeType != 3 && range.endOffset >= 1) {
        var nodeBeforeEnd = range.endContainer.childNodes[range.endOffset - 1];
        if (nodeBeforeEnd && nodeBeforeEnd.nodeType == 3) {
            range.setEnd(nodeBeforeEnd, nodeBeforeEnd.data.length);
        }
    }
    return range;
}


回答2:

Check this fiddle ( Updated )

I have made it in a way that it will work on triple click when paragraph contains multiple lines.

<script>
    function getSelRange() {
        var selObj = window.getSelection();
        var range = selObj.getRangeAt(0);
        var r = document.getElementById('txt').innerHTML.split('<br>');

        if (r[(range.endOffset-1)/2] == selObj) {
            alert(0+"-"+r[(range.endOffset-1)/2].length);
        } else if (range.startOffset >= range.endOffset) {
            alert(range.startOffset + "-" + r[(range.endOffset-1)/2].length);
        } else {
            alert(range.startOffset + "-" + range.endOffset);
        }  
    }
</script>

New Added Fiddle

<script>
function getSelRange() {
    var selObj = window.getSelection();
    var range  = selObj.getRangeAt(0);
    var r=document.getElementById('txt').innerHTML.split('<br>');
    var selLines = selObj.toString().split('\n');

    var Str = document.getElementById('txt').innerHTML;
    Str=Str.replace(/<br>/g,"xzznlzzx");
    var pr=selObj.toString().replace(/\r?\n/g,"xzznlzzx");
    var rStr=Str.substring(0,Str.indexOf(pr));

    var rSplit=rStr.split('xzznlzzx');
    var prSplit=pr.split('xzznlzzx');

    var countStart=0;
    var countEnd=0;
    var i=0;
    for(;i<(rSplit.length-1);i++)
    {
        countStart=countStart+r[i].length;
    }
    for(j=0;j<(prSplit.length-1);i++,j++)
    {
        countEnd=countEnd+r[i].length;
    }

    countEnd=countEnd+countStart;
    if(r[(range.endOffset-1)/2]==selObj)
    {
    alert((0+countStart)+"-"+(r[(range.endOffset-1)/2].length+countEnd));
    }
    else{
        if(r[i].length<selObj.toString().length)
        {
        var indx = selObj.toString().indexOf(r[i]);
        }
        else{
        var indx = r[i].indexOf(selObj.toString());
        var vals=selObj.toString().length;
        var res = r[i].substring(indx+vals,indx+vals+1);
        if(res==""){indx=1}
        else{indx=-1}
        }
        if(indx!=-1)
        {
        alert((range.startOffset+countStart)+"-"+(r[i].length+countEnd));
        }
        else{
        alert((range.startOffset+countStart)+"-"+(range.endOffset+countEnd));
        }
    } 
}
</script>

Note : for the above fiddles to work the string within <p> tag must be in a single line otherwise it will add the extra spaces between words to the range.