代碼和特性在chrome49下測試有效。
文本渲染的本質是對文本節(jié)點的渲染,通過瀏覽器內置的對象Range可以獲得選擇的起始點、與終止點
var range = getRangeObject();var start = range.startOffset, end = range.endOffset;var startContainer = range.startContainer;var endContainer = range.endContainer;
getRangeObjec代碼如下
function getRangeObject(){if(window.getSelection) {var selection = window.getSelection();if(selection.rangeCount > 0) {return selection.getRangeAt(0); } }else if(document.selection) {return document.selection.createRange(); }return null; };
起始點始終在左面,終止點始終在右面,不受選擇方向的影響。
只有當起始點的開頭或終止點的末尾是<br/>時,返回的不是文本節(jié)點,可以通過start,end確定br元素的位置分別是startContainer.childNodes[start],endContainer.childNodes[end-1]。返回的是文本節(jié)點start表示光標相對于起始文本節(jié)點所在的起始位置,end表示光標相對于終止文本節(jié)點所在的終止位置。
獲得下一個文本節(jié)點的算法為
function getNextTextNode(startNode,dir = "nextSibling"){//記錄startNode變化之前的狀態(tài),startNode變化后無效時便于狀態(tài)的回滾let unchangeNode = startNode;if(startNode.nodeType == 3){ startNode = startNode[dir]; }while (true){if(startNode == undefined){if(unchangeNode == undefined){//保護機制throw new Error("程序會陷入死循環(huán)");break; }/*startNode所在的父元素所有選中節(jié)點遍歷完畢,將sartNode指向父元素的兄弟節(jié)點*/let parent = unchangeNode.parentElement; unchangeNode = parent; startNode = parent[dir]; }else if(startNode.nodeType == 3){//文本節(jié)點則退出循環(huán)break; }else if(startNode.tagName == "BR"){//處理單標簽,避免不必要的迭代unchangeNode = startNode; startNode = startNode[dir]; }else if(startNode.nodeType == 1){/*如果是雙標簽元素則進入*/unchangeNode = startNode;if(dir == "previousSibling"){ startNode = $(startNode).contents().last().get(0); }else if(dir == "nextSibling"){ startNode = $(startNode).contents().first().get(0); }else {//便于錯誤的定位throw new Error("錯誤的遍歷方向:"+dir); } }else {//便于錯誤的定位throw new Error("不期待的元素類型=》"+startNode); } } return startNode; }
//上述函數用外部變量+while循環(huán)的方式取代遞歸,加入的保護機制減少誤用、潛在bug導致極差的體驗。
獲得起始節(jié)點和結束節(jié)點之間的所有文本節(jié)點
function getTextNodes(startTextNode,endTextNode){ let textNodeArray = []; let node = startTextNode;while (true) { node = getNextTextNode(node);if(node == endTextNode){break; } textNodeArray.push(node); } return textNodeArray; }