日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java实现给选中文字添加样式,天坑之路:用js给选中文字添加样式

發(fā)布時間:2025/3/21 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java实现给选中文字添加样式,天坑之路:用js给选中文字添加样式 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

本例基于react,但是實(shí)際上就是用原生js做的。兼容性做到了IE9,但是按照這個思路做是可以做到IE8甚至更低的。

需求與最初的思路

當(dāng)我拿到這個需求的時候以為很簡單,就是可以給頁面上的文章做記號,比如添加個下劃線,或者背景涂色做成熒光筆的樣子。

因?yàn)橹恍枰嫒軮E9,所以window.getSelection是支持的。(IE8及以下有其它的獲取選中的方法)

那么思路就是選中文本,點(diǎn)擊添加下劃線后,通過 window.getSelection.getRangeAt(0) 拿到選中的文本對象,獲取到文本后,通過文本對象的 surroundContents 方法來將文本替換為帶有class的元素。

初步的實(shí)現(xiàn)

思路很簡單,代碼同樣也很簡單。

CSS代碼:.custom-underline{??border-bottom:?1px?solid?#f00;??font-style:?normal;

}.nite-writer-pen{??background-color:?lightgreen;??border-radius:?5px;??box-shadow:?0?0?10px?lightgreen;??font-style:?normal;

}

JS代碼:/**

*?用元素替換被選中的文本

*/var?replaceSelectedStrByEle?=?function(className){??var?selecter?=?window.getSelection();??var?selectStr?=?selecter.toString();??if?(selectStr.trim?!=?"")?{????var?rang?=?selecter.getRangeAt(0);????var?ele?=?document.createElement("i");

ele.className?=?className;

ele.textContent?=?selectStr

rang.surroundContents(ele);

}

}

replaceSelectedStrByEle('nite-writer-pen');

天坑出現(xiàn)

上面的思路實(shí)在是過于簡單,如果是一個很簡單的元素,那么這種做法是沒有問題的。

但是我們的文章的html結(jié)構(gòu)一般都沒有這么簡單,比如對于以下情況:

道可道,非常道。

名可名,非常名

如果在頁面上我選中的操作如下:

那么上面的代碼實(shí)現(xiàn)就會出現(xiàn)BUG,對于這種跨元素選中的情況,想當(dāng)然的用元素去替換文本是沒用的。

如果你想得更多,比如跨多個元素選中,以及選中元素為更為復(fù)雜的html結(jié)構(gòu),你就會發(fā)現(xiàn)這是一個多大的坑。

html結(jié)構(gòu)有多復(fù)雜,這個坑就有多深。

思路的僵局與寫輪眼

其實(shí)天坑也不是完全沒有路走,在跨多個元素選中的過程中,我想給選中的內(nèi)容加樣式,那么就需要獲取到所有選中的文本節(jié)點(diǎn),并且批量替換成元素。

但是 window.getSelection.getRangeAt(0) 獲取到的range對象只能獲取到最開始選中的節(jié)點(diǎn)和最后選中的節(jié)點(diǎn)的。

那么接下來通過選中的最開始的節(jié)點(diǎn)和最后的節(jié)點(diǎn)獲取到所有的文本節(jié)點(diǎn)。

思路就是這么個思路,但是實(shí)現(xiàn)起來是很復(fù)雜的。

面臨深坑,肯定不可能硬剛。

畢竟我已經(jīng)不是當(dāng)年頭鐵的愣頭青了,做項(xiàng)目是有時間和精力成本的,如果要填掉這個坑,那么加班是不可避免的,最重要的是在有限時間內(nèi)填的這個坑可能還有各種BUG和兼容性問題。

對待這種天坑,一般就給需求來個做不了三連了。

但是這把我想贏,畢竟這個東西看起來確實(shí)簡單。

在外人的眼里,如此之簡單,分分鐘搞定的事情。這都做不了,我還怎么在前端的圈子里繼續(xù)劃水?

所以我要動用程序員的入門技——寫輪眼。

然而百度、谷歌無效,根本沒有這個解決方案,有的全是些我最初的簡單實(shí)現(xiàn)。

但是回憶我們以前見到的各種網(wǎng)頁應(yīng)用與場景,很容易就能想到上面的這種操作我們是見過的。

那就是從遠(yuǎn)古IE時代就已經(jīng)出現(xiàn)的各種富文本編輯器組件。

目標(biāo)確認(rèn),百度的ueditor,這波我要贏。

分析ueditor與復(fù)制

上github兩三下拿到ueditor源碼,開始讀源碼分析代碼。

中間過程不再多說,精簡代碼,除去一些不需要的代碼和兼容性處理后,拿到了五個文件:browser.js (瀏覽器版本判斷,用于做兼容性處理)

domUtils.js (dom操作)

dtd.js (節(jié)點(diǎn)的類型與元素判斷)

Range.js (封裝的選中范圍對象)

utils.js (工具類)

即使精簡后,代碼也不少,大概兩三千行。不過其中還有很多注釋,壓縮后體積并不大。

由于代碼比較多,這里就不全部展示了,文章最后會給出github的地址。

這里只給出最后的使用代碼:/**

*?添加下劃線

*/addUnderline?=?()?=>?{??this.replaceSelectedStrByEle(styles['custom-underline'])

}/**

*?啟用熒光筆

*/enableNiteWriterPen?=?()?=>?{??this.replaceSelectedStrByEle(styles['nite-writer-pen'])

}/**

*?用元素替換被選中的文本

*/replaceSelectedStrByEle?=?(className)?=>?{??var?getRange?=?()?=>?{????var?me?=?window;????var?range?=?new?Range(me.document);????var?sel?=?window.getSelection();????if?(sel?&&?sel.rangeCount)?{??????var?firstRange?=?sel.getRangeAt(0);??????var?lastRange?=?sel.getRangeAt(sel.rangeCount?-?1);

range.setStart(firstRange.startContainer,?firstRange.startOffset)

.setEnd(lastRange.endContainer,?lastRange.endOffset);

}????return?range

}??var?range?=?getRange();

range.applyInlineStyle('i',?{????class:?className

});

range.select();

}

使用起來還是比較簡單的。

對i元素的處理做的一些修改

如果我們選中的是已經(jīng)被包裹在i元素中的一段文本,那么調(diào)用后會發(fā)現(xiàn)并沒有加上class屬性。

這是因?yàn)楦晃谋揪庉嬈骱臀覀兊男枨蟛灰粯?#xff0c;通過操作想把選中文本變?yōu)閕元素,而文本外面本來就是i元素了,自然不會進(jìn)行剩下的操作。

在填充元素的最后會有一個mergeToParent的操作,他會在填充元素的標(biāo)簽和其父級元素的標(biāo)簽一樣后將元素替換為文本。if?(parent.tagName?==?node.tagName?||?parent.tagName?==?"A")?{??//...}

那么這里我們要修改源碼加上一個判斷if?((parent.tagName?==?node.tagName?&&?parent.className?==?node.className)?||?parent.tagName?==?"A")?{??//...}

至于其它的邏輯保持不變即可。

為支持回退操作做的一些修改

這里getRange拿到的對象range在選中內(nèi)容并替換樣式后依然可以使用。

可以調(diào)用range.removeInlineStyle('i')

移除之前添加的樣式。

也就是說這里如果使用一個命令模式之類的,是可以實(shí)現(xiàn)回退操作的。

不過這里還是有一個坑,就是removeInlineStyle會移除掉選中內(nèi)容中所有的i元素,于是我修改了

Range.js中removeInlineStyle這個方法,多加了一個className參數(shù),每次去掉i元素時都會判斷是否參數(shù)等于className。

然后我們調(diào)用時就是range.removeInlineStyle('i',styles['nite-writer-pen'])

總結(jié)

作為一個暗藏天坑的小需求,搞定之后其實(shí)還挺有成就感的。

粗略閱讀了源碼后才發(fā)現(xiàn)如果自己做會有多坑,基本上沒個三五天下不來,并且在多掉了N根頭發(fā)后仍然會發(fā)現(xiàn)到處都是考慮不周和各種BUG。

《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

總結(jié)

以上是生活随笔為你收集整理的java实现给选中文字添加样式,天坑之路:用js给选中文字添加样式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。