计算可编辑div中范围(高亮显示的文本)的绝对开始和结束



我正在进行一个实验,看看我是否可以在一个contentitable(实际上对测试并不重要)div中返回一个突出显示的测试块的绝对起始点和结束点。我不是在构建一个富文本编辑器或任何东西,我只是想知道它是如何完成的!因此,我想在右键单击时返回(不重要,我只是把它弄乱了)是两个数字,包装器div开始到选择开始的绝对距离和包装器div开始到选择结束的绝对距离。

我认为Mootools会让这很容易,但我只能让他们的实现与表单(即文本区,输入等)工作。因此,我使用Google进行了快速测试,当不涉及标签时,一切都工作得很好,例如He| lo world |ld(其中管道|表示高亮显示的范围)将返回[2,9],这是正确的。然而,当我向div添加标签以允许颜色/格式化这些数字时,这些数字就没有任何意义了,因为范围只给出了相对于文本节点的位置,而不是绝对值。知道怎么得到这个吗?我只能想象它涉及到某种可怕的DOM操作。

JS:

window.addEvent('domready', function()
    {
        document.body.addEvent('contextmenu', 
            function(e)
            {
                e.stop();
            }
        );
        if(!window.SelectionHandler)
        {
            SelectionHandler = {};
        }
        SelectionHandler.Selector = {};
        SelectionHandler.Selector.getSelected = function()
        {
            var userSelection = '';
            if(window.getSelection)
            {
                userSelection = window.getSelection();
            }
            else if(document.getSelection)
            {
                userSelection = document.getSelection();
            }
            else if(document.selection)
            {
                userSelection = document.selection.createRange();
            }
            return userSelection;
        }
        SelectionHandler.Selector.getText = function(userSelection)
        {
            var selectedText = userSelection;
            if(userSelection.text)
            {
                selectedText = userSelection.text;
            }
            return selectedText;
        }
        SelectionHandler.Selector.getRange = function(userSelection)
        {
            if(userSelection.getRangeAt && typeof(userSelection.getRangeAt) != 'undefined')
            {
                var selectedRange = userSelection.getRangeAt(0);
            }
            else
            {
                var selectedRange = document.createRange();
                selectedRange.setStart(userSelection.anchorNode, userSelection.anchorOffset);
                selectedRange.setEnd(userSelection.focusNode, userSelection.focusOffset);
            }
            return selectedRange;
        }
        $('mydiv').addEvent('mousedown', 
            function(event)
            {
                if(event.rightClick)
                {
                    var userSelection   = SelectionHandler.Selector.getSelected();
                    var selectedText    = SelectionHandler.Selector.getText(userSelection);
                    var selectedRange   = SelectionHandler.Selector.getRange(userSelection);
                    // New ranges to add identifiable nodes (must be in that order!?)
                    var endRange = document.createRange();
                    endRange.setStart(selectedRange.endContainer, selectedRange.endOffset);
                    endRange.insertNode(document.createTextNode('!~'));
                    var startRange = document.createRange();
                    startRange.setStart(selectedRange.startContainer, selectedRange.startOffset);
                    startRange.insertNode(document.createTextNode('~!'));
                    // Find the position of our new identifiable nodes (and account for their removal)
                    var div_content = $('mydiv').get('html');
                    var start       = div_content.indexOf('~!');
                    var end         = div_content.indexOf('!~') - 2;
                    // Remove our identifiable nodes (DOESN'T WORK)
                    //startRange.deleteContents();
                    //endRange.deleteContents();
                    // This does work, but obviously loses the selection
                    div_content = div_content.replace('~!', '').replace('!~', '');
                    $('mydiv').set('html', div_content);
                    console.log(start + ' vs ' + end);
                }
            }
        );
    }
);
HTML:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <title>Edit Range Test</title>
 </head>
 <script type="text/javascript" src="mootools.js"></script>
 <script type="text/javascript" src="edit_range.js"></script>
 <style>
    #mydiv {
        width: 400px;
        height: 400px;
        border: 1px solid #a2a2a2;
        padding: 5px;
    }
 </style>
 <body>
    <h1>Edit Range Test</h1>
    <div id="mydiv" contentEditable="true"><span style="color: red;">Hello</span> World! <span style="color: red;">Hello</span> World! </div>
 </body>
</html>

所以当我现在选择He| lo Wor|ld(其中管道|再次代表高亮显示的范围)时,当我想要[28,42]时,它将返回[2,4]。

编辑:我已经更新了代码来澄清我要做的事情。它做了我想测试的大部分内容,但失去了选择,而且非常邋遢!

提前感谢!

首先,您从Range对象获得的信息非常有用:对于Range的每个开始和结束边界,您获得一个DOM节点和该节点内的偏移量(文本或注释节点内的字符偏移量或其他子节点偏移量),它完全描述了边界。你所说的"绝对起点和终点"是指整个可编辑元素中的两个字符偏移量,但这是一个模糊的概念:看起来很简单,但很难确定。例如,一个段落的换行符有多少个字符?A <br> ?一个<a>元素与display: block ?元素,如<script>元素,包含文本,但不可见的用户?通过display: none隐藏的元素?正常文档流之外的元素,例如通过position: absolute ?多个连续的空白字符被呈现为一个单一的可见字符的浏览器?

说了这么多,我最近尝试编写代码来实现这一点,是的,它确实涉及DOM操作(尽管不是那么可怕)。它非常简陋,不能令人满意地处理上面的任何问题,部分原因是我为了一个关于SO的问题很快就把它弄出来了,部分原因是我认为它不可能很好地处理所有这些问题。下面的答案提供了在可编辑元素中保存和恢复选择作为字符索引的函数:替换contentitablediv

中的innerHTML

最新更新