将标记转换为选择范围

时间:2012-11-17 23:24:24

标签: javascript jquery range selection rangy

如何将jQuery选择中的一组令牌范围转换为一组rangy范围?

例如我有这个:

<div class="test-input">
    <p>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas
        convallis dui id erat pellentesque et rhoncus nunc semper. Suspendisse
        malesuada {hendrerit velit nec }tristique. Aliq{uam gravida mauris at
        ligula venenatis rhoncus. Suspendisse inter}dum, nisi nec consectetur
        pulvinar, lorem augue ornare felis, vel lacinia erat nibh in ve{lit.
    </p>
    <p>
        Hendr}erit, felis ac fringilla lobortis, massa ligula aliquet justo, sit
        amet tincidunt enim quam {sollicitudin} nisi. Maecenas ipsum augue,
        commodo sit amet aliquet ut, laoreet ut nunc. Vestibulum ante ipsum
        primis in {fauc}ibus orci luctus et ultrices posuere cubilia Curae;
        Pellentesque tincidunt eros quis tellus laoreet ac dignissim turpis
        luctus. Integer nunc est, {pulvinar ac tempor ac, pretium ut odio.
    </p>
    <p>
        Pellentesque in arcu sit amet} odio scelerisque tincidunt. Lorem ipsum
        dolor sit amet, consectetur adipiscing elit. Pellentesque habitant morbi
        tristique senectus et netus et malesuada fames ac turpis egestas.
    </p>
</div>

我希望将{}之间的文本转换为范围(并删除令牌)。

我试过用这个:

function tokensToRanges(element) {
    element = $(element);
    var node = element.get(0);
    var ranges = [];
    do {
        var text = $(node).text(),
            start = text.indexOf('{'),
            end = text.indexOf('}') - 1,
            input = null;
        input = node.innerHTML.replace('{', '').replace('}', '');
        element.html(input);
        var range = rangy.createRange();
        range.selectCharacters(node, start, end);
        ranges.push(range);
    } while ($(node).text().indexOf('{') != -1);
    return ranges;
}

但它的范围不正确。我认为selectCharacters方法忽略了空格。

如果可能的话,我宁愿不使用TextRangeModule

1 个答案:

答案 0 :(得分:1)

selectCharacters()不会忽略所有空格,但会忽略折叠的空格。例如,如果文本节点包含三个连续的空格字符,则只有第一个空格字符有助于字符计数。我可以为该方法添加一个选项来关闭该行为。

在回答你的问题时,Rangy的测试套件的功能有点像你想要的,所以我在下面对它进行了改编。开始和结束范围标记可能出现在不同的节点中。

演示:http://jsfiddle.net/timdown/DdeFr/

代码:

function RangeInfo() {}

RangeInfo.prototype = {
    setStart: function(node, offset) {
        this.sc = node;
        this.so = offset;
    },
    setEnd: function(node, offset) {
        this.ec = node;
        this.eo = offset;
    },
    toRange: function() {
        var range = rangy.createRange();
        range.setStart(this.sc, this.so);
        range.setEnd(this.ec, this.eo);
        return range;
    }
};

function getTextNodesIn(node) {
    var textNodes = [];
    function getTextNodes(node) {
        if (node.nodeType === 3) {
            textNodes.push(node);
        } else {
            for (var i = 0, l = node.childNodes.length; i < l; i++) {
                getTextNodes(node.childNodes[i]);
            }
        }
    }

    getTextNodes(node);
    return textNodes;
}

function tokensToRanges(el) {
    var rangeInfos = [];
    var currentRangeInfo;
    var textNodes = getTextNodesIn(el);

    $.each(textNodes, function() {
        var searchStartIndex = 0;
        var searchIndex;
        while ( (searchIndex = this.data.indexOf(currentRangeInfo ? "}" : "{", searchStartIndex)) != -1 ) {
            // Remove the marker. Doing this breaks existing ranges
            // in this node, which is why we use RangeInfo objects
            // instead of ranges
            this.data = this.data.slice(0, searchIndex) + this.data.slice(searchIndex + 1);
            if (currentRangeInfo) {
                currentRangeInfo.setEnd(this, searchIndex);
                rangeInfos.push(currentRangeInfo);
                currentRangeInfo = null;
            } else {
                currentRangeInfo = new RangeInfo();
                currentRangeInfo.setStart(this, searchIndex);
            }
            searchStartIndex = searchIndex;
        }
    });

    // Convert RangeInfos into ranges
    var ranges = [];
    $.each(rangeInfos, function() {
        ranges.push(this.toRange());
    });

    return ranges;
}

var ranges = tokensToRanges(document.body);
var applier = rangy.createCssClassApplier("highlight");
applier.applyToRanges(ranges);