1. 程式人生 > 其它 >javascript 實現富文字框選中對齊

javascript 實現富文字框選中對齊

需求:

一個可編輯(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
    }