javascript 實現富文字框選中對齊
阿新 • • 發佈:2022-03-23
需求:
一個可編輯(contenteditable=true)的div,對齊選中內容,左、中,右 ,其實質是:對選中的末梢節點,找到塊屬性的父元素,設定text-algin:center:
MDN:
text-align
CSS屬性定義行內內容(例如文字)如何相對它的塊父元素對齊。text-align
並不控制塊元素自己的對齊,只控制它的行內內容的對齊。
分析需求:
我們來分解一下這個需求的三個關鍵問題: ”選中部分“,”塊元素“,"末梢元素"
1如何獲取選中的部分 *
這裡涉及到的Web API Document.getSelection().getRangeAt(0) 這裡只處理一個選取的情況
注意:游標所在位置,游標所在節點 視為選中區域
2什麼是塊元素
MDN:
display:block
這個值會生成一個塊級元素盒子,同時在該元素之前和之後打斷(換行)。簡單來說就是,這個值會將該元素變成塊級元素。
除非特殊指定,諸如標題(
<h1>
等)和段落(<p>
)預設情況下都是塊級的盒子。
用做連結的
<a>
元素、<span>
、<em>
以及<strong>
都是預設處於inline
狀態的。
3末梢元素(沒有子節點的元素)
我們操作對齊,實質是操作盒模型中的內容的對齊方式,也就是對:圖片,文字 等設定對齊樣式,在這裡我稱其為末梢節點
實現思路:
1、獲取選區內的所有末梢元素(遞迴)
2、找到這些末梢元素的父塊元素,設定其text-align:'left|center|right'
程式碼實現:
前端頁面:一個div contenteditable="true";三個按鈕:觸發對齊(左,中,右)
document.querySelector("#btn_alignl").addEventListener("click", () => { Align.call(this, 'left') }) document.querySelector("#btn_alignc").addEventListener("click", () => { Align.call(this, 'center') }) document.querySelector("#btn_alignr").addEventListener("click", () => { Align('right') })
js 程式碼:
1、一個公共的Align方法,引數為:left|center|right
/**
* 1 通過getEndNodes(PNode,startNode,endNode, ResultNodes)獲取PNode中在startNode和ResultNodes之間的所有末梢元素
* 2 遍歷EndNodes,通過getBlockParent獲取每一個endNode的父級block元素
* 3 設定endnode 的 blockparent.style.textAlign=left|center|right
* @param alignStr left|center|right
**/
function Align(alignStr) {
const rng = document.getSelection().getRangeAt(0)
const commonAncestor = rng.commonAncestorContainer
isStarted = false, isEnded = false
const startEndNode=getEndNodes(rng.startContainer,rng.startContainer,rng.startContainer,[])[0]
isStarted = false, isEnded = false
const endEndNode=getEndNodes(rng.endContainer,rng.endContainer,rng.endContainer,[])[0]
isStarted = false, isEnded = false
const EndNodes = getEndNodes(commonAncestor,startEndNode,endEndNode,[])
EndNodes.forEach(node => {
const blockparent = getBlockParent(node)
if (!!blockparent && blockparent.style.textAlign != alignStr) {
blockparent.style.textAlign = alignStr
}
})
}
獲取選中元素的末梢節點的方法
let isStarted = false, isEnded = false
function getEndNodes(PNode,startNode,endNode, ResultNodes) {
if (PNode == startNode) { isStarted = true }
if (PNode == endNode) {
isEnded = true
ResultNodes.push(PNode)
console.log(PNode)
}
if (isStarted == true && isEnded == false && PNode.hasChildNodes()==false) { ResultNodes.push(PNode) ;console.log(PNode)}
if (PNode.hasChildNodes() && isEnded==false) {
PNode.childNodes.forEach(node => {
getEndNodes(node, startNode,endNode, ResultNodes)
});
}
// debugger
return ResultNodes
}
getBlockParent的實現--獲取選中末梢節點的塊父節點的實現
let blockTags = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'ul', 'ol', 'li', 'div', 'body', 'td', 'th']
// let inlineTags = ['img', 'font', 'b', 'strong', 'span', 'a']
let blockTagSet = new Map()
blockTags.forEach((v) => { blockTagSet.set(v, true) });
const source = document.querySelector('div.source');
function getBlockParent(ele) {
let result = undefined
if (ele === source) {
console.log('已找到editor的根,並沒有找父級block元素');
result = undefined
} else {
switch (ele.nodeType) {
//element: 判斷ele是否是塊級元素,判斷依據1 display:block 2 預設的塊級元素
case 1: {
const disPro = ele.style.display;
if (disPro && disPro.toLowerCase().indexOf('block') > -1) {
result = ele;
} else if (blockTagSet.get(ele.tagName.toLowerCase())) { result = ele }
else { result = getBlockParent(ele.parentElement) }
break;
}
case 3: {//textNode
if (!!ele.nodeValue.trim())
result = getBlockParent(ele.parentElement)
else result = undefined
break;
}
default: {
break;
}
} //end switch
}//end if
return result
}