思路:

首先查找选中文本是以后html第几个字符,而后进行替换款式(isIE为true代表的是ie浏览器)
1、自定义右键事件
2、获取选中的文本及文本为以后html第几个
3、进行替换并勾销文本选中

自定义右键事件

document.oncontentmenu = function(e){    //判断是否有选中的文本    if(!this.judgeSelectText(this.getSelectedText())){return false};    this.getSelectedTextAndCurrentNum();    let target = e.target;    //判断创立菜单的类型;dom对象id是否蕴含"spanId_"    if(        this.selectText &&         ((target.id && target.id.indexOf("spanId_")<0) || !target.id)){        //创立长期右键菜单        this.createMenu("add",e,target)    }else if(target.id && target.id.indexOf("spanId_") > -1){        this.spanId = target.id;        this.createMenu("other",e,target)    }}.bind(this)//点击正文操作document.onmousedown = function(e){    let target = e.target;    //增加正文    if(target.id && target.id.indexOf("addAnnotate") >= -1){        if(this.selectText != ""){            //1、关上增加正文弹框,2、设置操作为增加            this.operation = "add";            }else if(target.id && target.id.indexOf("updateAnnotate") >= -1){            //1、关上批改正文弹框,2、设置操作为批改,3、申请获取正文数据值            this.operation = "update";            }else if(target.id && target.id.indexOf("deleteAnnotate") >= -1){            //1、关上删除正文弹框,2、设置操作为删除            this.operation = "delete";            }else if(target.id && target.id.indexOf("viewAnnotate") >= -1){            //1、关上查看正文弹框,2、设置操作为查看,3、申请获取正文数据值            this.operation = "view";            }    }}.bind(this)

getSelectedText:获取选中的数据

getSelectedText(){    if(isIE){        let selectedText = "";        if(document.selection){            selectedText = document.selection.createRange().text;        }else if(document.getSelection){            selectedText = document.getSelection().toString();        }else if(window.getSelection){            selectedText = window.getSelection().toString();        }        return selectedText    }else{        let selectObject = window.getSelection();        let selectedText = selectObject.toString();        return selectedText    }}

judgeSelectText:判断以后选中的数据是否符合规范

judgeSelectText(selectText){    //判断文本是否多段    if(selectText.indexOf("\r")>=0 || selectText.indexOf("\n")>=0){        alert("多段文本不反对增加正文")        return false;            }else if(window.getSelection()){        //判断以后选中数据是否含有正文字段        let selection = window.getSelection();        if(selection.anchorNode){            //anchorNode:返回该选区终点所在的节点            //focusNode:返回该选区起点所在的节点            if(selection.anchorNode.data != selection.focusNode.data){                alert("选中文本蕴含已正文的文本");                return false            }        }    }    return true}

getSelectedTextAndCurrentNum:获取以后选中的数据及地位放入内存

getSelectedTextAndCurrentNum(){    if(isIE){        //以后selection对象        let selectText = "";        let selectObject = null;        if(document.selection){            //ie10            selectText = document.selection.createRange().text;        }else if(document.getSelection){            //ie11            selectText = document.getSelection().toString();        }else if(window.getSelection){            selectText = window.getSelection().toString();        }        selectObject = document.getSelection()        if(selectText && selectText != ""){            this.selectText = selectText;            //获取比拟以后地位数据进行比拟            let compareData = this.getNowSelectionCompareData(selectObject);            //初始化设置以后地位为0,入选中文本为暗藏状态扭转后的数据(dispaly:none-->display:block);            //暗藏的文本计算不正确,hideCurentNum为暗藏文本数量            let currentNumber = 0;            this.hideCurentNum = 0;            //获取以后是第几个元素            let range = document.body.createRange();            currentNumber = this.getIECurrentNum(range,compareData,selectText,0);            this.currentNum = currentNumber        }else{            this.selectText = "";            this.currentNum = 0        }    }else{        let selectObject = window.getSelection();        let selectText = selectObject.toString();        if(selectText && selectText != ""){            this.selectText = selectText;            //获取比拟以后地位数据进行比拟            let compareData = this.getNowSelectionCompareData(selectObject);            let currentNumber = 0;            window.getSelection().removeAllRanges();            currentNumber = this.getNotIECurrentNum(compareData,selectText,0);            this.currentNum = currentNumber        }else{            this.selectText = "";            this.currentNum = 0        }    }}

getNowSelectionCompareData:获取比拟对象

getNowSelectionCompareData(selection){    let compareData = {};    if(selection){        var flg = selection.anchorOffset < selection.focusOffset;        compareData = {            leftPoint:flg ? selection.anchorOffset : selection.focusOffset,            rightPoint:flg ? selection.focusOffset : selection.anchorOffset,            leftPointNode:flg ? selection.anchorNode : selection.focusNode,            rightNode:flg ? selection.focusNode : selection.anchorNode,        }        return compareData;    }    return null;}

getIECurrentNum:IE浏览器计算以后选中为第几个compareData

getIECurrentNum(text,compareData,str,n){    if(text.findText(str)){        try{            text.select()        } catch(e) {            //报错阐明含有暗藏文本            this.hideCurentNum++;            text.collapse(false);            return this.getIECurrentNum(text,compareData,str,n)        }        n++;        var tmpSelection = document.getSelection();        if(            tmpSelection.focusNode == compareData.rightNode &&            tmpSelection.anchorNode == compareData.leftNode &&            tmpSelection.anchorOffset == compareData.leftPoint &&            tmpSelection.focusOffset == compareData.rightPoint        ){            document.body.createTextRange();            return n;        }else{            text.collapse(false);            return this.getIECurrentNum(text,compareData,str,n)        }    }else{        document.body.createTextRange();        text.findText(str);        return 0;    }}

getNotIECurrentNum:获取非ie以后页面选中字符的地位

getNotIECurrentNum(compareData,str,n){    if(window.find(str,false,false)){        n++;        var tmpSelection = widow.getSelection();        if(            tmpSelection.focusNode == compareData.rightNode &&            tmpSelection.anchorNode == compareData.leftNode &&            tmpSelection.anchorOffset == compareData.leftPoint &&            tmpSelection.focusOffset == compareData.rightPoint        ){            return n;        }else return this.getNotIECurrentNum(compareData,str,n)    }else return 0}

createMenu:创立菜单(依据absolute定位)

createMenu(operation,e,parentNode){    //屏幕宽度    let iframeWidth = document.documentElement.clientWidth ||     window.clientWidth ||     document.body.clientWidth;    //新增时,元素间隔父元素的高度    let topDistance = this.isIE       ? 10 :       e.clientY-parentNode.getBoundingClientRect().top;    //新增时,元素间隔父元素左侧间隔    let rightDistance = parentNode.getBoundingClientReact().right - e.offsetX;    let leftDistance;    if(      parentNode.targetName == "TD" ||      parentNode.parentNode.targetName == "TD" ||      parentNode.parentNode.parentNode.targetName == "TD"    ){        leftDistance =           parentNode.getBoundingClientRect().left < 150             ? e.offsetX            : e.offsetX - 150;    }else{        leftDistance =           e.offsetX < 300            ? e.offsetX             : rightDistance > 150             ? e.offsetX            : e.offsetX - 150    }    //编辑删除查看操作左侧间隔    let otherLeftDistance =       iframeWidth - parentNode.getBoundingClientRect().right > 150        ? 10        : -150;    this.removeMenu();    if(!parentNode) return ;    parentNode.style.position = "relative";    let tmpDiv = document.createElement("div");    if(operation == "add"){        tmpDiv.className = "previewMenuDiv";        tmpDiv.style.cssText =           "position:absolute;"+          "left:"+leftDistance+"px;"+          "top:"+topDistance+"px;"+          "z-index:99;text-indent:0";        tmpDiv.innerHTML =           "<div id='addAnnotate'>增加正文</div>"    }else{        tmpDiv.className = "previewMenuDiv";        tmpDiv.style.cssText =           "position:absolute;"+          "left:"+otherLeftDistance+"px;"+          "z-index:99;text-indent:0";        tmpDiv.innerHTML =           "<div id='viewAnnotate'>查看正文</div><div id='updateAnnotate'>编辑正文</div><div id='deleteAnnotate'>删除正文</div>";    }    if(parentNode){        parentNode.appendChild(tmpDiv)    }    this.previewMenu = tmpDiv;}

createMenu:移除菜单

createMenu(){    if(this.previewMenu){        this.preview.parentNode.removeChild(this.previewMenu);        this.previewMenu = null    }}

保留正文操作
正文id:spanId = "spanId_"+new Date().getTime();
选中文本:this.selectText;
选中数据为以后html第几个:currentNum;
非ie浏览器暗藏元素查找不到,ie浏览器需加上暗藏元素的个数hideCurrentNum

saveAnnotate(){    if(this.isIE){        this.replaceIEHtml(          this.currentNum + this.hideCurrentNum,          this.selectText,          this.spanId        )    }else{        this.replaceNotIEHtml(          this.currentNum,          this.selectText,          this.spanId        )    }}

replaceIEHtml:ie浏览器替换选中的元素

replaceIEHtml(currentNum,str,spanId){    var textRangeArr = [];    var range = document.body.crateTextRange();    textRangeArr = this.getFindTextRange(range,str);    let newTextRangeArr = [];    if(textRangeArr.length > 0){        for(let i = 0;i<textRangeArr.length;i++){            if(textRangeArr[i].offsetLeft != 0 && textRangeArr[i].offsetTop != 0){                newTextRangeArr.push(textRangeArr[i])            }        }    }    if(currentNum <= textRangeArr.length){        currentNum = currentNum > 0 ? currentNum : 1;        var nowText = newTextRangeArr[currentNum - 1];        var replaceSelectHtml =           '<span id="'+spanId+          '"oncontextmenu="" class="annotateKeyClass" style="background:#f28109">'+          str+"</span>";        if(nowText){            nowText.pasteHTML(replaceSelectHtml)        }    }}

getFindTextRange:获取以后ie符合条件的ranges

getFindTextRange(range,str){    var ranges = new Array();    var dup = range.duplicate();    var flg = true;    do{        var d = dup.duplicate();        if(d.findText(str)){            ranges.push(d);            dup.setEndPoint("StartToEnd",d);            continue;        }        break;    }while(flg);    return ranges;}

replaceNotIEHtml:非ie浏览器替换选中的元素

replaceNotIEHtml(currentNum,str,spanId){    var selectText = null;    window.getSelection().removeAllRanges();    selectText = this.getCurrentSession(str,0,currentNum);    if(selectText){        var range = selectText.getRangeAt(0);        var replaceTempSpan = document.createElement("span");        replaceTempSpan.id = spanId;        replaceTempSpan.className = "annotateKeyClass";        replaceTempSpan.style["background"] = "#f28109";        replaceTempSpan.innerText = str;        range.surroundContents(replaceTempSpan)    }    window.getSelection().removeAllRanges();}

getCurrentSession:获取以后正文的选中对象

getCurrentSession(str,n,currentNum){    if(window.find(str,false,false)){        n++;        if(n == currentNum){            return window.getSelection()        }        return this.getCurrentSession(str,n,currentNum)    }else{        return null;    }}