zoukankan      html  css  js  c++  java
  • 获取输入框中选中文本相对于页面的偏移

    题目:计算下图所示输入框中的选中文本相对于页面的偏移位置:

    textarea

    首先思考如何获取页面上选中文本相对页面的偏移位置。

    很多人可能知道 Element.getBoundingClientRect(),但这个方法是获取已知元素相对页面的偏移位置,所以在这里不能使用这个方法。

    其实 Range 也有一个 Range.getBoundingClientRect() 方法,它是获取 Range 相对于页面的偏移位置,又可以根据 Selection 得到 Range,所以问题似乎迎刃而解了。

    对于 IE 来说,问题是解决了,因为 IE 有一个叫做 createTextRange 方法,它对于输入框中的选中文本也是有效的。但 Webkit 中却没有这个方法,似乎没有什么完美的方法能解决这个问题。今天要讨论的就是如何在 Chrome ( Webkit ) 中解决这个问题。

    解决的思路如下:

    1. 使用 div 克隆输入框,即使 div 看上去和输入框如出一辙:边框、字体、颜色、内容等。
    2. 根据输入框的 selectionStart 和 selectionEnd 值,选中 div 中对应的内容。
    3. 获取 Selection 得到 Range,再使用 Range.getBoundingClientRect() 方法获取偏移值。
    4. 删除 div 元素,根据之前保存的输入框的 selectionStart 和 selectionEnd 值,选中输入框之前选中的文本。

    代码实现如下:

    $(function () {
        var getSelectedTextBounding = function (input, start, end) {
            var inputBoudRect = input.getBoundingClientRect();
            var div = $('<div>').html(input.value.replace(/
    /g, '<br />')).appendTo(document.body);
            div[0].style.cssText = document.defaultView.getComputedStyle(input, null).cssText;
            div.css({
                position: 'absolute',
                left: inputBoudRect.left,
                top: inputBoudRect.top,
                margin: 0,
                overflow: 'hidden'
            });
            div[0].scrollLeft = input.scrollLeft;
            div[0].scrollTop = input.scrollTop;
            var range = setSelectionRange(div[0], start, end);
            var textBounding = range.getBoundingClientRect();
            div.remove();
            return textBounding;
    
            function getTextNodesIn(node) {
                var textNodes = [];
                if (node.nodeType == 3) {
                    textNodes.push(node);
                } else {
                    var children = node.childNodes;
                    for (var i = 0, len = children.length; i < len; ++i) {
                        textNodes.push.apply(textNodes, getTextNodesIn(children[i]));
                    }
                }
                return textNodes;
            }
    
            function setSelectionRange(el, start, end) {
                var range = document.createRange();
                range.selectNodeContents(el);
                var textNodes = getTextNodesIn(el);
                var foundStart = false;
                var charCount = 0, endCharCount;
    
                for (var i = 0, textNode; textNode = textNodes[i++];) {
                    endCharCount = charCount + textNode.length;
                    if (!foundStart && start >= charCount && (start < endCharCount || (start == endCharCount && i < textNodes.length))) {
                        range.setStart(textNode, start - charCount);
                        foundStart = true;
                    }
                    if (foundStart && end <= endCharCount) {
                        range.setEnd(textNode, end - charCount);
                        break;
                    }
                    charCount = endCharCount;
                }
    
                var sel = window.getSelection();
                sel.removeAllRanges();
                sel.addRange(range);
                return range;
            }
        }
    
    
        var mouseDownedInput = null;
    
        $(document).on('mousedown', 'input, textarea', function () {
            mouseDownedInput = this;
        });
    
        $(document).mouseup(function (e) {
            var sel = window.getSelection();
            var selectedText = sel.toString().trim();
            var boundingClientRect;
            if (selectedText) {
                var r = sel.getRangeAt(0);
                if (r.collapsed) {
                    if (mouseDownedInput) {
                        var start = mouseDownedInput.selectionStart;
                        var end = mouseDownedInput.selectionEnd;
                        boundingClientRect = getSelectedTextBounding(mouseDownedInput, start, end);
                        mouseDownedInput.setSelectionRange(start, end);
                    }
                } else {
                    boundingClientRect = r.getBoundingClientRect();
                }
            }
            console.log(boundingClientRect)
            mouseDownedInput = null;
        });
    

    说明

    • 代码默认使用 jQuery 库。
    • 上面的代码并没完美地解决问题,主要是使用 div 模拟 textarea (或者input)时会遇到一些问题,比如 textarea 中的换行,div 中需要转换成 <br> 标签,这期间会丢失精度(当然也可以多写些代码解决这个精度问题,但这里不再讨论)。
    • 其他的都是一些细节问题,也有可能没有测试到,有遗漏也在所难免。若有问题,欢迎给我留言,非常感谢。
    • 这段代码已经用在了我开发的 Chrome 扩展中,欢迎使用:饥猪阅读,github地址:https://github.com/huntbao/piggyreader

    参考资料

  • 相关阅读:
    Jsp补充
    Jsp和Servlet关系
    ServletContext简介
    利用Session实现三天免登陆
    Cookie简介
    JSP简介
    线程基础--同步机制
    深思——工作面试
    response.setcontenttype的參数
    Codeforces 96D Volleyball spfa
  • 原文地址:https://www.cnblogs.com/huntbao/p/get-input-selected-text-offset.html
Copyright © 2011-2022 走看看