09月11, 2016

选中文本换背景色的实现

最近遇到的一个需求是,选择某段文字中的部分,为选中区域加颜色

主要是用到window.getSelection()rangeObject

主要方法

需要区分高级浏览器和IE。这里只讨论高级浏览器下的用法。

  • userSelection = window.getSelection()
  • userSelection.toString()
  • rangeObject = userSelection.getRangeAt(0)
  • rangeObject.startContainer
  • rangeObject.getClientRects()
  • rangeObject.deleteContents()
  • rangeObject.insertNode(span)

  • MDN Range

  • MDN Selection
  • 《Javasctipt高级程序设计》P438

代码

前方高能,大段代码。。。

/*是否在合法选择范围内*/
function rangeIsOk(sContainer, eContainer){

}

$('html, body').on('mousedown', function(e){
    if(!$(e.target).is('.tip-entity-list *')){
        // 先清空选区,否则再次点击选区会再次获得选区内容
        userSelection && userSelection.removeAllRanges();
        selTip.hide();
    }else if($(e.target).is('*[unselectable=on]')){
        e.preventDefault();
        return false;
    }
});


$('body').on('mouseup', function(e){
    var me = $(this);

    // 优先用高级浏览器的
    if(window.getSelection()){
        userSelection = window.getSelection();
        selText = userSelection.toString();
    }

    // 没有选中的文字,或者click事件直接返回
    if(!selText.length) return false;

        // 获得多个选区中的第一个选区
    rangeObject = userSelection;
    if (userSelection && userSelection.getRangeAt) {
        rangeObject = userSelection.getRangeAt(0);
    }

    if(!rangeObject.collapsed){
        var sContainer = rangeObject.startContainer.parentNode;
        var eContainer = rangeObject.endContainer.parentNode;

        // 下面这个是为了定位选中后提示框的位置
        if(rangeIsOk(sContainer, eContainer)){
                // 获得选中区域的一个矩阵
            var rangeRects = rangeObject.getClientRects();
            var maxTop = 0, num = 0, pHeight = 20;
            for(var i = 0, len = rangeRects.length; i < len; i++){
                //pHeight:一行文字的高度,为了避免双击选中整个大的区域
                if(rangeRects[i].height <= pHeight && rangeRects[i].width && maxTop < rangeRects[i].top){
                    maxTop = rangeRects[i].top;
                    num = i;
                }
            }

            var lastLeft = Math.floor(rangeRects[num].left + rangeRects[num].width);
            var lastTop =  Math.ceil(rangeRects[num].top + rangeRects[num].height);

            tipX = lastLeft;
            tipY = lastTop + $(window).scrollTop();

            var tipEntityLen = $('.tip-entity-item').length;
            if(tipEntityLen){
                // 不trim会获得选中的空格
                if($.trim(selText)){
                    tipShow(selText, tipX, tipY);
                }
            }else{
                // open dialog
                showTipPanel($("#messPanel"), "没有待选项");
            }

        }
    }

});

function tipShow(text, tipX, tipY){
    selTip.css('left',tipX).css('top',tipY).show();
}
// 加上颜色
$('body').on('click', '.tip-entity-item', function(e){

    var me = $(this);
    var color = me.attr('data-color');
    var title = me.attr('data-title');
    var type = me.attr('data-type');

    var sContainer = rangeObject.startContainer.parentNode;
    var eContainer = rangeObject.endContainer.parentNode;

    if(rangeIsOk(sContainer, eContainer)){
        var span = document.createElement('span');
        span.innerHTML = selText;
        span.style.background = color;
        span.title = title;
        span.setAttribute('data-eid', type);

        rangeObject.deleteContents();
        rangeObject.insertNode(span);

    }else if(sNodeName == 'SPAN' || eNodeName == 'SPAN'){
        alert('已经有选中了的,点击取消后再进行重新选择')
    }

    selTip.hide();
});
递归的为某节点添加不可选择属性
function makeUnselectable(node) {
        //Node.ELEMENT_NODE
    if (node.nodeType == 1) {
        node.setAttribute("unselectable", "on");
    }
    var child = node.firstChild;
    while (child) {
        makeUnselectable(child);
        child = child.nextSibling;
    }
}

本文链接:https://imjiaolong.cn/post/range.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。