首页 > 获取页面中被选中文字的相对位置

获取页面中被选中文字的相对位置

<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Odit, saepe, tempore, quam, similique perferendis possimus rem optio harum sapiente hic dicta temporibus quae sit neque eaque illo ex 你好 asperiores consectetur nemo ullam odio delectus distinctio enim quasi cumque accusantium deleniti voluptatum eligendi sunt quibusdam</p>

在页面中选中了<p>标签里的“你好”后,我想要返回它在页面中的相对位置。
要用到什么方法。


getSelection() 可以捕捉到光标选择内容的位置,也能拿到选择内容所处的 DOM 标签,也能拿到选择内容前的文字。

  1. 先算出 容器DOM 所处页面的相对位置
  2. 新设一个 DIV,样式和 容器DOM 要一致,然后把选择内容前的文字放进去,容器DOM 的高度 + 该 DIV 的高度 就是选择文字的高度了
  3. 记下上一个 DIV 的宽度,然后再设置一个 span 保证内容在一行然后得到这个 span 的宽度,并以 DIV 的宽度取余,这样 容器DOM 的宽度 + DIV 宽度 % span 宽度 就是选择文字的宽度了。

记得每次处理完之后记得把过程中的 DIV 和 span 都删除掉,这样应该能应付绝大多数的场景了。


「被选中文字的相对位置」,位置(Position)既可理解爲相對於給定原點的笛卡爾坐標系的坐標(Coordinate, 公子的答案),也可以理解爲以字符爲基本單位的坐標(Offset, 如選擇了字符從第 10 到第 12)。

對於前者:

Range.getBoundingClientRect()

若老舊瀏覽器不支持,可以使用 span 包圍選區(Range.surroundContents()),然後 Element.getBoundingClientRect()。

下面是對後者的答案:

因爲涉及到任意富文本,我寫了一個通用富文本光標抽象(來自 http://.com/blog/bum/1190000000764811)。

var c = document.querySelector("code");

var Caret = function() {
    function some(a, f, u) {
        if (u === void(0)) u = false;
        for (var i = 0, n = a.length, r; i < n; ++i) if ((r = f(a[i], i, n)) !== u) return r;
        return u;
    }

    function getOffset(c, co, cc) {
        var cto = co, cp;
        if (c.nodeType !== 3) co = 0, some(c.childNodes, function(x, i) {
            if (i === cto) return true;
            co += x.textContent.length;
            return false;
        });
        return c === cc || cc && !cc.contains(c) ? co : function get(c, co, cc) {
            var cp = c.parentNode;
            if (c !== cp.firstChild) some(cp.childNodes, function(x) {
                if (x === c) return true;
                co += x.textContent.length;
                return false;
            });
            return cc && cc !== cp && cc.contains(cp) ? get(cp, co, cc) : co;
        }(c, co, cc);
    }

    function setOffset(cc, co, set, tion) {
        var to = co, c = cc;
        some(cc.childNodes, function(x, i, n) {
            var l = x.textContent.length; c = x;
            if (tion) {
                if (to < l) return true;
                if (to !== l || i !== n - 1) to -= l;
            } else {
                if (to <= l) return true;
                to -= l;
            }
            return false;
        });
        return c === cc || c.nodeType === 3 ? (set(c, to), c) : c = setOffset(c, to, set, tion);
    }

    return { get: getOffset, set: setOffset };
}();

function getCaret(x) {
    var s = window.getSelection(), r = s.getRangeAt(0);

    return (x.contains(r.startContainer)) ? 
        [Caret.get(r.startContainer, r.startOffset, x), Caret.get(r.endContainer, r.endOffset, x)] : false;
}

function setCaret(ro, y) {
    var s = window.getSelection(), r;

    if (ro) {
        if (document.activeElement !== c)
            c.focus();

        s.removeAllRanges();

        r = document.createRange();

        Caret.set(y, ro[0], r.setStart.bind(r));

        if (ro[0] === ro[1])
            r.collapse(true);
        else
            Caret.set(y, ro[1], r.setEnd.bind(r));

        s.addRange(r);
    }
}

獲取光標相對於元素的位置(當光標包含在該元素中時):getCaret(c)
設置光標相對於元素的位置:setCaret([caretStart, caretEnd], c)

【热门文章】
【热门文章】