/*
* 開發(fā)調(diào)試
*/
import React, { useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import styles from './style.module.less';
const keyword = ['const'];
const replaceBlank = (html) => html.replace(/ /ig, ' ');
// 獲取光標(biāo)位置
function getCaretCharacterOffsetWithin(element) {
let caretOffset = 0;
const doc = element.ownerDocument || element.document;
const win = doc.defaultView || doc.parentWindow;
let sel;
if (typeof win.getSelection !== 'undefined') {
sel = win.getSelection();
if (sel.rangeCount > 0) {
const range = win.getSelection().getRangeAt(0);
const preCaretRange = range.cloneRange();
preCaretRange.selectNodeContents(element);
preCaretRange.setEnd(range.endContainer, range.endOffset);
caretOffset = preCaretRange.toString().length;
}
} else if ((sel = doc.selection) && sel.type != 'Control') {
const textRange = sel.createRange();
const preCaretTextRange = doc.body.createTextRange();
preCaretTextRange.moveToElementText(element);
preCaretTextRange.setEndPoint('EndToEnd', textRange);
caretOffset = preCaretTextRange.text.length;
}
return caretOffset;
}
// 設(shè)置光標(biāo)位置
function setCaretPosition(element, offset) {
const range = document.createRange();
const sel = window.getSelection();
// select appropriate node
let currentNode = null;
let previousNode = null;
for (let i = 0; i < element.childNodes.length; i++) {
// save previous node
previousNode = currentNode;
// get current node
currentNode = element.childNodes[i];
// if we get span or something else then we should get child node
while (currentNode.childNodes.length > 0) {
[currentNode] = currentNode.childNodes;
}
// calc offset in current node
if (previousNode != null) {
offset -= previousNode.length;
}
// check whether current node has enough length
if (offset <= currentNode.length) {
break;
}
}
// move caret to specified offset
if (currentNode != null) {
range.setStart(currentNode, offset);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
}
// 排序方法
function compareWordLength(a, b) {
if (a.length > b.length) {
return -1;
} if (a.length < b.length) {
return 1;
}
return 0;
}
// 高亮關(guān)鍵字
function addKeyWordHighline(oText, keyWords) {
let returnVal = oText;
let i = 0;
let wordReg;
keyWords.sort(compareWordLength);
for (i = 0; i < keyWords.length; i++) {
if (keyWords[i] !== '') {
wordReg = new RegExp(`(?!<span+>.[^<]*)${keyWords[i]}(?!.[^<]*<\/span>)`, 'g');
returnVal = returnVal.replace(wordReg, `<span>${keyWords[i]}</span>`);
}
}
return returnVal;
}
const CodeInput = ({
value,
disable,
children,
onChange,
}) => {
const ref = useRef();
// 是否鎖定輸入
const isLock = useRef(false);
const getRef = () => ref && ref.current;
const onCompositionstart = (e) => {
isLock.current = true;
};
const onCompositionend = (e) => {
isLock.current = false;
};
// 解決中文輸入的時(shí)候腐晾,直接輸出英文字母的問題(中文輸入期間存谎,不允許輸入字符)
useEffect(() => {
// 監(jiān)聽中文輸入
const el = getRef();
el.addEventListener('compositionstart', onCompositionstart, false);
el.addEventListener('compositionend', onCompositionend, false);
return () => {
el.removeEventListener('compositionstart', onCompositionstart, false);
el.removeEventListener('compositionend', onCompositionend, false);
};
}, []);
const onInput = () => {
const el = getRef();
// dom是否為空 || 是否為鎖定模式
if (!el || isLock.current) return;
// 獲取內(nèi)容
let text = el.innerHTML;
// 是否修改了
if (value !== text) {
// 獲取光標(biāo)
const position = getCaretCharacterOffsetWithin(el);
// 替換空格
text = replaceBlank(text);
// 替換關(guān)鍵字
text = addKeyWordHighline(text, keyword);
el.innerHTML = text;
// 更新父組件
onChange(text);
// 恢復(fù)位置
setCaretPosition(el, position);
}
};
return (
<pre
ref={ref}
className={styles['formula-input']}
contentEditable={!disable}
dangerouslySetInnerHTML={{ __html: value }}
onInput={onInput}
>
{children}
</pre>
);
};
CodeInput.propTypes = {
value: PropTypes.array,
disable: PropTypes.number,
onChange: PropTypes.func,
children: PropTypes.node,
};
CodeInput.defaultProps = {
value: '默認(rèn)', // value
disable: false, // 是否可用
children: undefined, // 子元素
onChange: () => {},
};
export default CodeInput;
react contentEditable 可輸入div 高亮關(guān)鍵字
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
- 文/潘曉璐 我一進(jìn)店門含末,熙熙樓的掌柜王于貴愁眉苦臉地迎上來猜拾,“玉大人,你說我怎么就攤上這事答渔」卮” “怎么了?”我有些...
- 文/不壞的土叔 我叫張陵,是天一觀的道長宋雏。 經(jīng)常有香客問我芜飘,道長,這世上最難降的妖魔是什么磨总? 我笑而不...
- 正文 為了忘掉前任嗦明,我火速辦了婚禮,結(jié)果婚禮上蚪燕,老公的妹妹穿的比我還像新娘娶牌。我一直安慰自己,他們只是感情好馆纳,可當(dāng)我...
- 文/花漫 我一把揭開白布诗良。 她就那樣靜靜地躺著,像睡著了一般鲁驶。 火紅的嫁衣襯著肌膚如雪鉴裹。 梳的紋絲不亂的頭發(fā)上,一...
- 文/蒼蘭香墨 我猛地睜開眼鹦马,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了玖院?” 一聲冷哼從身側(cè)響起菠红,我...
- 序言:老撾萬榮一對(duì)情侶失蹤第岖,失蹤者是張志新(化名)和其女友劉穎难菌,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蔑滓,經(jīng)...
- 正文 獨(dú)居荒郊野嶺守林人離奇死亡郊酒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
- 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了键袱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片燎窘。...
- 正文 年R本政府宣布蚜迅,位于F島的核電站舵匾,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏谁不。R本人自食惡果不足惜坐梯,卻給世界環(huán)境...
- 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望刹帕。 院中可真熱鬧吵血,春花似錦、人聲如沸偷溺。這莊子的主人今日做“春日...
- 文/蒼蘭香墨 我抬頭看了看天上的太陽挫掏。三九已至晕翠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間砍濒,已是汗流浹背淋肾。 一陣腳步聲響...
- 正文 我出身青樓,卻偏偏與公主長得像杠河,于是被迫代替她去往敵國和親碌尔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
推薦閱讀更多精彩內(nèi)容
- 項(xiàng)目中要求實(shí)現(xiàn)一個(gè)功能券敌,在輸入框中輸入特定的搜索語句唾戚,當(dāng)輸入為關(guān)鍵字時(shí),關(guān)鍵字高亮待诅,且輸入滿一行進(jìn)行換行操作 首先...
- 最近項(xiàng)目當(dāng)中遇到一個(gè)需求要求實(shí)現(xiàn)輸入的內(nèi)容叹坦,如果接口返回的結(jié)果有關(guān)鍵字,則輸入的關(guān)鍵字高亮展示卑雁;實(shí)現(xiàn)出來感覺還是蠻...
- 在安卓的webView中自帶的有搜索關(guān)鍵字、顯示關(guān)鍵字以及滾動(dòng)扣甲,而iOS中webView并沒有這些功能篮赢,需要開發(fā)者...
- 參考: Swift - 給UITextView添加自定義鏈接启泣,以及鏈接的點(diǎn)擊響應(yīng) 控制器中配置TextView調(diào)用...
- 做項(xiàng)目過程中有個(gè)需求是:輸入一篇文章的關(guān)鍵字(可以是多個(gè)) 自己寫了個(gè)小組件媒咳,發(fā)布出去了,自測比較實(shí)用种远,有此需求的...