国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

利用 javascript 實(shí)現(xiàn)富文本編輯器

用戶84 / 1399人閱讀

摘要:考察了下現(xiàn)有的富文本編輯器,桌面端的很多,移動(dòng)端的幾乎沒(méi)有。為此決定自研一個(gè)富文本編輯器。本文,主要介紹如何實(shí)現(xiàn)富文本編輯器,和解決一些不同瀏覽器和設(shè)備之間的。光標(biāo)操作作為富文本編輯器,開(kāi)發(fā)者需要有能力控制光標(biāo)的各種狀態(tài)信息,位置信息等。

利用 javascript 實(shí)現(xiàn)富文本編輯器

閱讀 994
收藏 148
2017-11-03
原文鏈接:eux.baidu.com
利用 javascript 實(shí)現(xiàn)富文本編輯器
by.田 光宇 28 小時(shí)前
近期項(xiàng)目中需要開(kāi)發(fā)一個(gè)兼容PC和移動(dòng)端的富文本編輯器,其中包含了一些特殊的定制功能。考察了下現(xiàn)有的js富文本編輯器,桌面端的很多,移動(dòng)端的幾乎沒(méi)有。桌面端以UEditor為代表。但是我們并不打算考慮兼容性,所以沒(méi)有必要采用UEditor這么重的插件。為此決定自研一個(gè)富文本編輯器。本文,主要介紹如何實(shí)現(xiàn)富文本編輯器,和解決一些不同瀏覽器和設(shè)備之間的bug。

準(zhǔn)備階段
在現(xiàn)代瀏覽器中已經(jīng)為我們準(zhǔn)備好了許多API來(lái)讓 html 支持富文本編輯功能,我們沒(méi)有必要自己完成全部?jī)?nèi)容。

contenteditable=”true”

首先我們需要讓一個(gè) div 成為可編輯狀態(tài),加入contenteditable="true" 屬性即可。


在這樣的
中插入任何節(jié)點(diǎn)都將默認(rèn)是可編輯狀態(tài)的。如果想插入不可編輯的節(jié)點(diǎn),我們就需要指定插入節(jié)點(diǎn)的屬性為 contenteditable="false"。

光標(biāo)操作

作為富文本編輯器,開(kāi)發(fā)者需要有能力控制光標(biāo)的各種狀態(tài)信息,位置信息等。瀏覽器提供了 selection 對(duì)象和 range 對(duì)象來(lái)操作光標(biāo)。

selection 對(duì)象

Selection對(duì)象表示用戶選擇的文本范圍或插入符號(hào)的當(dāng)前位置。它代表頁(yè)面中的文本選區(qū),可能橫跨多個(gè)元素。文本選區(qū)由用戶拖拽鼠標(biāo)經(jīng)過(guò)文字而產(chǎn)生。
獲得一個(gè) selection 對(duì)象

let selection = window.getSelection();
通常情況下我們不會(huì)直接操作 selection 對(duì)象,而是需要操作用 seleciton 對(duì)象所對(duì)應(yīng)的用戶選擇的 ranges (區(qū)域),俗稱”拖藍(lán)“。獲取方式如下:

let range = selection.getRangeAt(0);
由于瀏覽器當(dāng)前可能存在多個(gè)文本選取,所以 getRangeAt 函數(shù)接受一個(gè)索引值。在富文本編輯其中,我們不考慮多選取的可能性。

selection 對(duì)象還有兩個(gè)重要的方法, addRange 和 removeAllRanges。分別用于向當(dāng)前選取添加一個(gè) range 對(duì)象和 刪除所有 range 對(duì)象。之后你會(huì)看到他們的用途。

range 對(duì)象

通過(guò) selection 對(duì)象獲得的 range 對(duì)象才是我們操作光標(biāo)的重點(diǎn)。Range表示包含節(jié)點(diǎn)和部分文本節(jié)點(diǎn)的文檔片段。初見(jiàn) range 對(duì)象你有可能會(huì)感到陌生又熟悉,在哪兒看見(jiàn)過(guò)呢?作為一個(gè)前端工程師,想必你一定拜讀過(guò)《javascript 高級(jí)程序設(shè)計(jì)第三版》 這本書(shū)。在第12.4節(jié),作者為我們介紹了 DOM2 級(jí)提供的 range 接口,用來(lái)更好的控制頁(yè)面。反正我當(dāng)時(shí)看的一臉????這個(gè)有啥用,也沒(méi)有這種需求啊。這里我們就大量的用到這個(gè)對(duì)象。對(duì)于下面節(jié)點(diǎn):

百度EUX團(tuán)隊(duì)


光標(biāo)位置如圖所示:

打印出此時(shí)的 range 對(duì)象:

其中屬性含義如下:

startContainer: range 范圍的起始節(jié)點(diǎn)。

endContainer: range 范圍的結(jié)束節(jié)點(diǎn)

startOffset: range 起點(diǎn)位置的偏移量。

endOffset: range 終點(diǎn)位置的偏移量。

commonAncestorContainer: 返回包含 startContainer 和 endContainer 的最深的節(jié)點(diǎn)。

collapsed: 返回一個(gè)用于判斷 Range 起始位置和終止位置是否相同的布爾值。

這里我們的 startContainer , endContainer, commonAncestorContainer都為 #text 文本節(jié)點(diǎn) ‘百度EUX團(tuán)隊(duì)’。因?yàn)楣鈽?biāo)在‘度‘字后面,所以startOffset 和 endOffset 均為 2。且沒(méi)有產(chǎn)生拖藍(lán),所以 collapsed 的值為 true。我們?cè)倏匆粋€(gè)產(chǎn)生拖藍(lán)的例子:

光標(biāo)位置如圖所示:

打印出此時(shí)的 range 對(duì)象:

由于產(chǎn)生了拖藍(lán) startContainer 和 endContainer 不再一致,collapsed 的值變?yōu)榱?false。startOffset 和 endOffset 正好代表了拖藍(lán)的起終位置。更多的效果大家自己嘗試吧。

操作一個(gè) range 節(jié)點(diǎn),主要有如下方法:

setStart(): 設(shè)置 Range 的起點(diǎn)
setEnd(): 設(shè)置 Range 的終點(diǎn)
selectNode(): 設(shè)定一個(gè)包含節(jié)點(diǎn)和節(jié)點(diǎn)內(nèi)容的 Range
collapse(): 向指定端點(diǎn)折疊該 Range
insertNode(): 在 Range 的起點(diǎn)處插入節(jié)點(diǎn)。
cloneRange(): 返回?fù)碛泻驮?Range 相同端點(diǎn)的克隆 Range 對(duì)象
富文本編輯里面常用的就這么多,還有很多方法就不列舉了。

修改光標(biāo)位置

我們可以通過(guò)調(diào)用 setStart() 和 setEnd() 方法,來(lái)修改一個(gè)光標(biāo)的位置或拖藍(lán)范圍。這兩個(gè)方法接受的參數(shù)為各自的起終節(jié)點(diǎn)和偏移量。例如我想讓光標(biāo)位置到”百度EUX團(tuán)隊(duì)”最末尾,那么可以采用如下方法:

let range = window.getSelection().getRangeAt(0),

textEle = range.commonAncestorContainer;

range.setStart(range.startContainer, textEle.length);
range.setEnd(range.endContainer, textEle.length);
我們加入一個(gè)定時(shí)器來(lái)查看效果:

然而這種方式有個(gè)局限性,就是當(dāng)光標(biāo)所在的節(jié)點(diǎn)如果發(fā)生了變動(dòng)。比如被替換或者加入新的節(jié)點(diǎn)了,那么再用這種方式就不會(huì)有任何效果。為此我們有時(shí)候需要一種強(qiáng)制更改光標(biāo)位置手段, 簡(jiǎn)要代碼如下(實(shí)際中你有可能還需要考慮自閉和元素等內(nèi)容):

function resetRange(startContainer, startOffset, endContainer, endOffset) {

let selection = window.getSelection();
    selection.removeAllRanges();
let range = document.createRange();
range.setStart(startContainer, startOffset);
range.setEnd(endContainer, endOffset);
selection.addRange(range);

}
我們通過(guò)重新創(chuàng)造一個(gè) range 對(duì)象并且刪除原有的 ranges 來(lái)保證光標(biāo)一定會(huì)變動(dòng)到我們想要的位置。

修改文本格式

實(shí)現(xiàn)富文本編輯器,我們就要能夠有修改文檔格式的能力,比如加粗,斜體,文本顏色,列表等內(nèi)容。DOM 為可編輯區(qū)提供了 document.execCommand 方法,該方法允許運(yùn)行命令來(lái)操縱可編輯區(qū)域的內(nèi)容。大多數(shù)命令影響文檔的選擇(粗體,斜體等),而其他命令插入新元素(添加鏈接)或影響整行(縮進(jìn))。當(dāng)使用 contentEditable時(shí),調(diào)用 execCommand() 將影響當(dāng)前活動(dòng)的可編輯元素。語(yǔ)法如下:

bool = document.execCommand(aCommandName, aShowDefaultUI, aValueArgument)
aCommandName: 一個(gè) DOMString ,命令的名稱。可用命令列表請(qǐng)參閱 命令 。
aShowDefaultUI: 一個(gè) Boolean, 是否展示用戶界面,一般為 false。Mozilla 沒(méi)有實(shí)現(xiàn)。
aValueArgument: 一些命令(例如insertImage)需要額外的參數(shù)(insertImage需要提供插入image的url),默認(rèn)為null。
總之瀏覽器能把大部分我們想到的富文本編輯器需要的功能都實(shí)現(xiàn)了,這里我就不一一演示了。感興趣的同學(xué)可以查看 MDN – document.execCommand。

到這里,我相信你已經(jīng)可以做出一個(gè)像模像樣的富文本編輯器了。想想還挺激動(dòng)的,但是呢,一切都沒(méi)有結(jié)束,瀏覽器又一次坑了我們。

實(shí)戰(zhàn)開(kāi)始,填坑的旅途
就在我們都以為開(kāi)發(fā)如此簡(jiǎn)單的時(shí)候,實(shí)際上手卻遇到了許多坑。

修正瀏覽器的默認(rèn)效果

瀏覽器提供的富文本效果并不總是好用的,下面介紹幾個(gè)遇到的問(wèn)題。

回車(chē)換行

當(dāng)我們?cè)诰庉嬈渲休斎雰?nèi)容并回車(chē)換行繼續(xù)輸入后,可編輯框內(nèi)容生成的節(jié)點(diǎn)和我們預(yù)期是不符的。

可以看到最先輸入的文字沒(méi)有被包裹起來(lái),而換行產(chǎn)生的內(nèi)容,包裹元素是

標(biāo)簽。為了能夠讓文字被

元素包裹起來(lái)。
我們要在初始化的時(shí)候,向

默認(rèn)插入


元素(
標(biāo)簽用來(lái)占位,有內(nèi)容輸入后會(huì)自動(dòng)刪除)。這樣以后每次回車(chē)產(chǎn)生的新內(nèi)容都會(huì)被

元素包裹起來(lái)(在可編輯狀態(tài)下,回車(chē)換行產(chǎn)生的新結(jié)構(gòu)會(huì)默認(rèn)拷貝之前的內(nèi)容,包裹節(jié)點(diǎn),類名等各種內(nèi)容)。
我們還需要監(jiān)聽(tīng) keyUp 事件下 event.keyCode === 8 刪除鍵。當(dāng)編輯器中內(nèi)容全被清空后(delete鍵也會(huì)把

標(biāo)簽刪除),要重新加入


標(biāo)簽,并把光標(biāo)定位在里面。

插入 ul 和 ol 位置錯(cuò)誤

當(dāng)我們調(diào)用 document.execCommand("insertUnorderedList", false, null) 來(lái)插入一個(gè)列表的時(shí)候,新的列表會(huì)被插入

標(biāo)簽中。

為此我們需要每次調(diào)用該命令前做一次修正,參考代碼如下:

function adjustList() {

let lists = document.querySelectorAll("ol, ul");
 for (let i = 0; i < lists.length; i++) {
    let ele = lists[i]; // ol
    let parentNode = ele.parentNode;
    if (parentNode.tagName === "P" && parentNode.lastChild === parentNode.firstChild) {
            parentNode.insertAdjacentElement("beforebegin", ele);
            parentNode.remove()
    }
}

}
這里有個(gè)附帶的小問(wèn)題,我試圖在

  • 維護(hù)這樣的編輯器結(jié)構(gòu)(默認(rèn)是沒(méi)有

    標(biāo)簽的)。效果在 chrome 下運(yùn)行很好。但是在 safari 中,回車(chē)永遠(yuǎn)不會(huì)產(chǎn)生新的

  • 標(biāo)簽,這樣就是去了該有的列表效果。

    插入分割線

    調(diào)用 document.execCommand("insertHorizontalRule", false, null); 會(huì)插入一個(gè)


    標(biāo)簽。然而產(chǎn)生的效果卻是這樣的:

    光標(biāo)和


    的效果一致了。為此要判斷當(dāng)前光標(biāo)是否在
  • 里面,如果是則在
    后面追加一個(gè)空的文本節(jié)點(diǎn) #text 不是的話追加


    。然后將光標(biāo)定位在里面,可用如下方式查找。

    /**

    查找父元素

    @param {String} root

    @param {String | Array} name

    */
    function findParentByTagName(root, name) {

    let parent = root;
    if (typeof name === "string") {
        name = [name];
    }
    while (name.indexOf(parent.nodeName.toLowerCase()) === -1 && parent.nodeName !== "BODY" && parent.nodeName !== "HTML") {
        parent = parent.parentNode;
    }
    return parent.nodeName === "BODY" || parent.nodeName === "HTML" ? null : parent;

    },
    插入鏈接

    調(diào)用 document.execCommand("createLink", false, url); 方法我們可以插入一個(gè) url 鏈接,但是該方法不支持插入指定文字的鏈接。同時(shí)對(duì)已經(jīng)有鏈接的位置可以反復(fù)插入新的鏈接。為此我們需要重寫(xiě)此方法。

    function insertLink(url, title) {

    let selection = document.getSelection(),
        range = selection.getRangeAt(0);
    if(range.collapsed) {
        let start = range.startContainer,
            parent = Util.findParentByTagName(start, "a");
        if(parent) {
            parent.setAttribute("src", url);
        }else {
            this.insertHTML(`${title}`);
        }
    }else {
        document.execCommand("createLink", false, url);
    }

    }
    設(shè)置 h1 ~ h6 標(biāo)題

    瀏覽器沒(méi)有現(xiàn)成的方法,但我們可以借助 document.execCommand("formatBlock", false, tag), 來(lái)實(shí)現(xiàn),代碼如下:

    function setHeading(heading) {

    let formatTag = heading,
        formatBlock = document.queryCommandValue("formatBlock");
    if (formatBlock.length > 0 && formatBlock.toLowerCase() === formatTag) {
        document.execCommand("formatBlock", false, ``);
    } else {
        document.execCommand("formatBlock", false, ``);
    }

    }
    插入定制內(nèi)容

    當(dāng)編輯器上傳或加載附件的時(shí)候,要插入能夠展示附件的

    節(jié)點(diǎn)卡片到編輯中。這里我們借助 document.execCommand("insertHTML", false, html); 來(lái)插入內(nèi)容。為了防止div被編輯,要設(shè)置 contenteditable="false"哦。

    處理 paste 粘貼

    在富文本編輯器中,粘貼效果默認(rèn)采用如下規(guī)則:

    如果是帶有格式的文本,則保留格式(格式會(huì)被轉(zhuǎn)換成html標(biāo)簽的形式)
    粘貼圖文混排的內(nèi)容,圖片可以顯示,src 為圖片真實(shí)地址。
    通過(guò)復(fù)制圖片來(lái)進(jìn)行粘貼的時(shí)候,不能粘入內(nèi)容
    粘貼其他格式內(nèi)容,不能粘入內(nèi)容
    為了能夠控制粘貼的內(nèi)容,我們監(jiān)聽(tīng) paste 事件。該事件的 event 對(duì)象中會(huì)包含一個(gè) clipboardData 剪切板對(duì)象。我們可以利用該對(duì)象的 getData 方法來(lái)獲得帶有格式和不帶格式的內(nèi)容,如下。

    let plainText = event.clipboardData.getData("text/plain"); // 無(wú)格式文本
    let plainHTML = event.clipboardData.getData("text/html"); // 有格式文本
    之后調(diào)用 document.execCommand("insertText", false, plainText); 或 document.execCommand("insertHTML", false, plainHTML; 來(lái)重寫(xiě)編輯上的paste效果。

    然而對(duì)于規(guī)則 3 ,上述方案就無(wú)法處理了。這里我們要引入 event.clipboardData.items 。這是一個(gè)數(shù)組包含了所有剪切板中的內(nèi)容對(duì)象。比如你復(fù)制了一張圖片來(lái)粘貼,那么 event.clipboardData.items 的長(zhǎng)度就為2:
    items[0] 為圖片的名稱,items[0].kind 為 ‘string’, items[0].type 為 ‘text/plain’ 或 ‘text/html’。獲取內(nèi)容方式如下:

    items[0].getAsString(str => {

    // 處理 str 即可

    })
    items[1] 為圖片的二進(jìn)制數(shù)據(jù),items[1].kind 為’file’, items[1].type 為圖片的格式。想要獲取里面的內(nèi)容,我們就需要?jiǎng)?chuàng)建 FileReader 對(duì)象了。示例代碼如下:

    let file = items[1].getAsFile();
    // file.size 為文件大小
    let reader = new FileReader();
    reader.onload = function() {

    // reader.result 為文件內(nèi)容,就可以做上傳操作了

    }
    if(/image/.test(item.type)) {

    reader.readAsDataURL(file);   // 讀取為 base64 格式

    }
    處理完圖片,那么對(duì)于復(fù)制粘貼其他格式內(nèi)容會(huì)怎么樣呢?在 mac 中,如果你復(fù)制一個(gè)磁盤(pán)文件,event.clipboardData.items 的長(zhǎng)度為 2。 items[0] 依然為文件名,然而 items[1] 則為圖片了,沒(méi)錯(cuò),是文件的縮略圖。

    輸入法處理

    當(dāng)使用輸入發(fā)的時(shí)候,有時(shí)候會(huì)發(fā)生一些意想不到的事情。 比如百度輸入法可以輸入一張本地圖片,為此我們需要監(jiān)聽(tīng)輸入法產(chǎn)生的內(nèi)容做處理。這里通過(guò)如下兩個(gè)事件處理:

    compositionstart: 當(dāng)瀏覽器有非直接的文字輸入時(shí), compositionstart事件會(huì)以同步模式觸發(fā)
    compositionend: 當(dāng)瀏覽器是直接的文字輸入時(shí), compositionend會(huì)以同步模式觸發(fā)
    修復(fù)移動(dòng)端的問(wèn)題

    在移動(dòng)端,富文本編輯器的問(wèn)題主要集中在光標(biāo)和鍵盤(pán)上面。我這里介紹幾個(gè)比較大的坑。

    自動(dòng)獲取焦點(diǎn)

    如果想讓我們的編輯器自動(dòng)獲得焦點(diǎn),彈出軟鍵盤(pán),可以利用 focus() 方法。然而在 ios 下,死活沒(méi)有結(jié)果。這主要是因?yàn)?ios safari 中,為了安全考慮不允許代碼獲得焦點(diǎn)。只能通過(guò)用戶交互點(diǎn)擊才可以。還好,這一限制可以去除:

    [self.appWebView setKeyboardDisplayRequiresUserAction:NO]
    iOS 下回車(chē)換行,滾動(dòng)條不會(huì)自動(dòng)滾動(dòng)

    在 iOS 下,當(dāng)我們回車(chē)換行的時(shí)候,滾動(dòng)條并不會(huì)隨著滾動(dòng)下去。這樣光標(biāo)就可能被鍵盤(pán)擋住,體驗(yàn)不好。為了解決這一問(wèn)題,我們就需要監(jiān)聽(tīng) selectionchange 事件,觸發(fā)時(shí),計(jì)算每次光標(biāo)編輯器頂端距離,之后再調(diào)用 window.scroll() 即可解決。問(wèn)題在于我們要如何計(jì)算當(dāng)前光標(biāo)的位置,如果僅是計(jì)算光標(biāo)所在父元素的位置很有可能出現(xiàn)偏差(多行文本計(jì)算不準(zhǔn))。我們可以通過(guò)創(chuàng)建一個(gè)臨時(shí) 元素查到光標(biāo)位置,計(jì)算元素的位置即可。代碼如下:

    function getCaretYPosition() {

    let sel = window.getSelection(),
        range = sel.getRangeAt(0);
    let span = document.createElement("span");
    range.collapse(false);
    range.insertNode(span);
    var topPosition = span.offsetTop;
    span.parentNode.removeChild(span);
    return topPosition;

    }
    正當(dāng)我開(kāi)心的時(shí)候,安卓端反應(yīng),編輯器越編輯越卡。什么鬼?我在 chrome 上線檢查了一下,發(fā)現(xiàn) selectionchange 函數(shù)一直在運(yùn)行,不管有沒(méi)有操作。
    在逐一排查的時(shí)候發(fā)現(xiàn)了這么一個(gè)事實(shí)。range.insertNode 函數(shù)同樣觸發(fā) selectionchange 事件。這樣就形成了一個(gè)死循環(huán)。這個(gè)死循環(huán)在 safari 中就不會(huì)產(chǎn)生,只出現(xiàn)在 safari 中,為此我們就需要加上瀏覽器類型判斷了。

    鍵盤(pán)彈起遮擋輸入部分

    網(wǎng)上對(duì)于這個(gè)問(wèn)題主要的方案就是,設(shè)置定時(shí)器。局限與前端,確實(shí)只能這采用這樣笨笨的解決。最后我們讓 iOS 同學(xué)在鍵盤(pán)彈出的時(shí)候,將 webview 高度減去軟鍵盤(pán)高度就解決了。

    CGFloat webviewY = 64.0 + self.noteSourceView.height;
    self.appWebView.frame = CGRectMake(0, webviewY, BDScreenWidth, BDScreenHeight - webviewY - height);
    插入圖片失敗

    在移動(dòng)端,通過(guò)調(diào)用 jsbridge 來(lái)喚起相冊(cè)選擇圖片。之后調(diào)用 insertImage 函數(shù)來(lái)向編輯器插入圖片。然而,插入圖片一直失敗。最后發(fā)現(xiàn)是因?yàn)樵?safari 下,如果編輯器失去了焦點(diǎn),那么 selection 和 range 對(duì)象將銷(xiāo)毀。因此調(diào)用 insertImage 時(shí),并不能獲得光標(biāo)所在位置,因此失敗。為此需要增加,backupRange() 和 restoreRange() 函數(shù)。當(dāng)頁(yè)面失去焦點(diǎn)的時(shí)候記錄 range 信息,插入圖片前恢復(fù) range 信息。

    backupRange() {

    let selection = window.getSelection();
    let range = selection.getRangeAt(0);
    this.currentSelection = {
        "startContainer": range.startContainer,
        "startOffset": range.startOffset,
        "endContainer": range.endContainer,
        "endOffset": range.endOffset
    }

    }
    restoreRange() {

    if (this.currentSelection) {
        let selection = window.getSelection();
            selection.removeAllRanges();
        let range = document.createRange();
        range.setStart(this.currentSelection.startContainer, this.currentSelection.startOffset);
        range.setEnd(this.currentSelection.endContainer, this.currentSelection.endOffset);
        // 向選區(qū)中添加一個(gè)區(qū)域
        selection.addRange(range);
    }

    }
    在 chrome 中,失去焦點(diǎn)并不會(huì)清除 seleciton 對(duì)象和 range 對(duì)象,這樣我們輕輕松松一個(gè) focus() 就搞定了。

    重要問(wèn)題就這么多,限于篇幅限制其他的問(wèn)題省略了。總體來(lái)說(shuō),填坑花了開(kāi)發(fā)的大部分時(shí)間。

    其他功能
    基礎(chǔ)功能修修補(bǔ)補(bǔ)以后,實(shí)際項(xiàng)目中有可能遇到一些其他的需求,比如當(dāng)前光標(biāo)所在文字內(nèi)容狀態(tài)啊,圖片拖拽放大啊,待辦列表功能,附件卡片等功能啊,markdown切換等等。在了解了js 富文本的種種坑之后,range 對(duì)象的操作之后,相信這些問(wèn)題你都可以輕松解決。這里最后提幾個(gè)做擴(kuò)展功能時(shí)候遇到的有去的問(wèn)題。

    回車(chē)換行帶格式

    前面已經(jīng)說(shuō)過(guò)了,富文本編輯器的機(jī)制就是這樣,當(dāng)你回車(chē)換行的時(shí)候新產(chǎn)生的內(nèi)容和之前的格式一模一樣。如果我們利用 .card 類來(lái)定義了一個(gè)卡片內(nèi)容,那么換行產(chǎn)生的新的段落都將含有 .card 類且結(jié)構(gòu)也是直接 copy 過(guò)來(lái)的。我們想要屏蔽這種機(jī)制,于是嘗試在 keydown 的階段做處理(如果在 keyup 階段處理用戶體驗(yàn)不好)。然而,并沒(méi)有什么用,因?yàn)橛脩糇远x的 keydown 事件要在 瀏覽器富文本的默認(rèn) keydown 事件之前觸發(fā),這樣你就做不了任何處理。
    為此我們?yōu)檫@類特殊的個(gè)體都添加一個(gè) property 屬性,添加在 property 上的內(nèi)容是不會(huì)被copy下來(lái)的。這樣以后就可以區(qū)分出來(lái)了,從而做對(duì)應(yīng)的處理。

    獲取當(dāng)前光標(biāo)所在處樣式

    這里主要是考慮 下劃線,刪除線之類的樣式,這些樣式都是用標(biāo)簽類描述的,所以要遍歷標(biāo)簽層級(jí)。直接上代碼:

    function getCaretStyle() {

    let selection = window.getSelection(),
        range = selection.getRangeAt(0);
        aimEle = range.commonAncestorContainer,
        tempEle = null;
    let tags = ["U", "I", "B", "STRIKE"],
        result = [];
    if(aimEle.nodeType === 3) {
        aimEle = aimEle.parentNode;
    }
    tempEle = aimEle;
    while(block.indexOf(tempEle.nodeName.toLowerCase()) === -1) {
        if(tags.indexOf(tempEle.nodeName) !== -1) {
            result.push(tempEle.nodeName);
        }
        tempEle = tempEle.parentNode;
    }
    let viewStyle = {
        "italic": result.indexOf("I") !== -1 ? true : false,
        "underline": result.indexOf("U") !== -1 ? true : false,
        "bold": result.indexOf("B") !== -1 ? true : false,
        "strike": result.indexOf("STRIKE") !== -1 ? true : false
    }
    let styles = window.getComputedStyle(aimEle, null);
    viewStyle.fontSize = styles["fontSize"],
    viewStyle.color = styles["color"],
    viewStyle.fontWeight = styles["fontWeight"],
    viewStyle.fontStyle = styles["fontStyle"],
    viewStyle.textDecoration = styles["textDecoration"];
    viewStyle.isH1 = Util.findParentByTagName(aimEle, "h1") ? true : false;
    viewStyle.isH2 = Util.findParentByTagName(aimEle, "h2") ? true : false;
    viewStyle.isP = Util.findParentByTagName(aimEle, "p") ? true : false;
    viewStyle.isUl = Util.findParentByTagName(aimEle, "ul") ? true : false;
    viewStyle.isOl = Util.findParentByTagName(aimEle, "ol") ? true : false;
    return viewStyle;

    }

    文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

    轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/53712.html

  • 相關(guān)文章

    • 利用 javascript 實(shí)現(xiàn)文本輯器

      摘要:考察了下現(xiàn)有的富文本編輯器,桌面端的很多,移動(dòng)端的幾乎沒(méi)有。為此決定自研一個(gè)富文本編輯器。本文,主要介紹如何實(shí)現(xiàn)富文本編輯器,和解決一些不同瀏覽器和設(shè)備之間的。光標(biāo)操作作為富文本編輯器,開(kāi)發(fā)者需要有能力控制光標(biāo)的各種狀態(tài)信息,位置信息等。 利用 javascript 實(shí)現(xiàn)富文本編輯器 閱讀 994收藏 1482017-11-03原文鏈接:eux.baidu.com利用 javascri...

      cyixlq 評(píng)論0 收藏0
    • 利用 javascript 實(shí)現(xiàn)文本輯器

      摘要:考察了下現(xiàn)有的富文本編輯器,桌面端的很多,移動(dòng)端的幾乎沒(méi)有。為此決定自研一個(gè)富文本編輯器。本文,主要介紹如何實(shí)現(xiàn)富文本編輯器,和解決一些不同瀏覽器和設(shè)備之間的。光標(biāo)操作作為富文本編輯器,開(kāi)發(fā)者需要有能力控制光標(biāo)的各種狀態(tài)信息,位置信息等。 利用 javascript 實(shí)現(xiàn)富文本編輯器 閱讀 994收藏 1482017-11-03原文鏈接:eux.baidu.com利用 javascri...

      voidking 評(píng)論0 收藏0
    • 開(kāi)發(fā)文本輯器的一些經(jīng)驗(yàn)教訓(xùn)

      摘要:當(dāng)然,這只是結(jié)合自己項(xiàng)目的工程結(jié)構(gòu)和特點(diǎn)設(shè)置的一套使用方式,僅供參考開(kāi)發(fā)富文本編輯器的教訓(xùn)由于項(xiàng)目的時(shí)間較緊張,我在頁(yè)面上應(yīng)用了框架的背景下,想當(dāng)然的想要把也應(yīng)用于富文本編輯器的開(kāi)發(fā),事實(shí)證明這是不太可行的。 此文已由作者劉詩(shī)川授權(quán)網(wǎng)易云社區(qū)發(fā)布。 歡迎訪問(wèn)網(wǎng)易云社區(qū),了解更多網(wǎng)易技術(shù)產(chǎn)品運(yùn)營(yíng)經(jīng)驗(yàn)。 最近我們的產(chǎn)品有一個(gè)需求是要在PC端做一個(gè)面向用戶的書(shū)評(píng)編輯器,讓用戶和編輯在蝸牛讀書(shū)...

      mtunique 評(píng)論0 收藏0
    • 推薦輕量高效無(wú)依賴的開(kāi)源JS插件和庫(kù)

      摘要:彈出層是一個(gè)輕量級(jí)的庫(kù)用于管理工具提示和彈窗效果。一個(gè)帶有的跨瀏覽器富文本編輯器。由制作,適用于每天寫(xiě)作的富文本編輯器。輕量的操作庫(kù)。是一個(gè)快速簡(jiǎn)單輕量級(jí)的瀏覽器功能檢測(cè)庫(kù)。它沒(méi)有任何的依賴,并且壓縮后僅有。極小跨平臺(tái)的全屏插件。 在這里維持一個(gè)持續(xù)更新的地方 圖片 baguetteBox.js - 是一個(gè)簡(jiǎn)單易用的響應(yīng)式圖像燈箱效果腳本。demo Lightgallery.js -...

      AlphaWallet 評(píng)論0 收藏0

    發(fā)表評(píng)論

    0條評(píng)論

    最新活動(dòng)
    閱讀需要支付1元查看
    <