textarea怎么解析html代码,从而实现一个可高亮的输入框
效果:

思路: 让一个div浮动在textarea上,样式和位置保持完全一致,textarea负责输入,div负责高亮显示
代码:
.vue
<template> <div class="highlight-contain"> <!-- 本组件是带高亮的textarea,需要接受高亮关键词数组来进行高亮 --> <div id="highlight-area" class="input-font el-textarea" > <div id="fake-textarea" class="el-textarea__inner" v-html="highlightHtml"></div> </div> <div id="input-area"> <el-input class="input-font" id="input-textarea" type="textarea" :placeholder="placeholder" :autosize="autosize" v-model="strValue" @input="getHighlightHtml" @mousemove.native="setHighlightArea('height',true)" ></el-input> </div> </div> </template> <style lang="postcss"> /* 这里是为了让textarea中的文字隐藏,同时这只光标和placeholder颜色 */ #input-area .el-textarea .el-textarea__inner { color: #606266; /* 光标的颜色*/ text-shadow: 0px 0px 0px rgba(0, 0, 0, 0); /* 文本颜色 */ -webkit-text-fill-color: transparent; &::-webkit-input-placeholder { color: #dcdfe6; /* 改变placeholder文本颜色 */ text-shadow: none; -webkit-text-fill-color: initial; } } .highlight-contain { position: relative; & #highlight-area { /* 自定义样式 */ position: absolute; left: 0px; top: 0px; pointer-events: none; & #fake-textarea { /* color: #ec140d; */ pointer-events: none; border: none; resize: none; background-color: rgba(0, 0, 0, 0); line-height: 1.5 !important; max-height: 590px; overflow-y: auto; /* 和html中的类一样,都是为了设定和textarea一模一样的样式,防止文字对接不上(这里是复制的浏览器中textarea元素属性) */ -webkit-appearance: textarea; -webkit-rtl-ordering: logical; -webkit-writing-mode: horizontal-tb !important; flex-direction: column; white-space: pre-wrap; word-wrap: break-word; text-rendering: auto; letter-spacing: normal; word-spacing: normal; text-transform: none; text-indent: 0px; text-shadow: none; text-align: start; margin: 0em; font: 400 14px Arial; } } } /* 高亮色 */ .highlight-11 { color: #fb4546; } .highlight-10 { color: #0fed40; } .highlight-9 { color: #feb71d; } .highlight-8 { color: #39afea; } .highlight-7 { color: #e512bf; } .highlight-6 { color: #0f29ed; } .highlight-5 { color: #f088c1; } .highlight-4 { color: #acbb09; } .highlight-3 { color: #7a152e; } .highlight-2 { color: #7ca51c; } .highlight-1 { color: #5e36aa; } .highlight-bracket { color: #000000; } </style> <script lang="ts" src="./highlightTextarea.ts"></script>
.ts
import { Vue, Component, Prop } from "vue-property-decorator"
import $ from 'jquery'
@Component({})
export default class HighlightTextarea extends Vue {
/* ---- 从父元素接受参数 ---- */
@Prop()
value: string
@Prop()
placeholder: string
@Prop()
autosize: { minRows: number, maxRows: number }
@Prop()
highlightKey: string[] //要高亮的词
/* ---- 变量 ---- */
get strValue() {
return this.value ? this.value : ''
}
set strValue(val) {
this.$emit('input', val)
}
highlightHtml: string = '';
/* ---- 函数 ---- */
setHighlightArea(type: string, right?: boolean) {
if (type === 'height') {
if (right) {
let height = document.getElementById('input-textarea').style.height;
document.getElementById('fake-textarea').style.height = height;
} else {
window.setTimeout(() => {
let height = document.getElementById('input-textarea').style.height;
document.getElementById('fake-textarea').style.height = height;
}, 100);
}
} else if (type === 'scrollTop') {
if (right) {
let scroll = document.getElementById('input-textarea').scrollTop
document.getElementById('fake-textarea').scrollTop = scroll;
} else {
window.setTimeout(() => {
let scroll = document.getElementById('input-textarea').scrollTop
document.getElementById('fake-textarea').scrollTop = scroll;
}, 100);
}
}
}
getHighlightHtml(val) {
if (val.split('\n').length > this.autosize.maxRows) { //超过最大行textarea有滚动时,为解决div底部不能和textarea重合,故加一个<br/>,并延时设置scrolltop
this.highlightHtml = this.highlightStr(val, this.highlightKey) + '<br/>';
this.setHighlightArea('scrollTop', false);
} else {
this.highlightHtml = this.highlightStr(val, this.highlightKey)
}
// 高亮区和输入区高度保持一致
this.setHighlightArea('height');
}
/**
* 高亮方法:
* 1.将oriStr中的高亮关键字使用“{{{关键字}}}”替换,这里防止关键词数组中有包含关系,所有用空格区分oriStr
* 2. 然后再循环highlightKey用<span class="..."></span>替换文中的{{{和}}}
* @param oriStr 要高亮的字符串
* @param highlightKey 高亮关键词
*/
highlightStr(oriStr: string, highlightKey: string[]): string {
if (!oriStr || !highlightKey || highlightKey.length === 0)
return oriStr;
let strConvert = (s: string, key: string): string => {
let rowArr = s.split('\n'); //按行进行处理
for (let i = 0; i < rowArr.length; i++) {
let strArr = rowArr[i].split(' ').filter(item => item !== '');
strArr = strArr.map(item => {
if (item === key) {
item = `{{{${item}}}}`
}
return item;
})
rowArr[i] = strArr.join(' ')
}
return rowArr.join('\n');
}
let rebuild = highlightKey.reduce(strConvert, oriStr);
let regExp;
let regStr;
for (let i = 0; i < highlightKey.length; i++) {
regStr = '\\{\\{\\{' + this.escapeString(highlightKey[i]);
regExp = new RegExp(regStr, 'g');
if (highlightKey[i] === '(' || highlightKey[i] === ')') { //小括号颜色
rebuild = rebuild.replace(regExp, `<span class="highlight-bracket">${highlightKey[i]}</span>`)
} else {
rebuild = rebuild.replace(regExp, `<span class="highlight-${i + 1}">${highlightKey[i]}`)
}
}
rebuild = rebuild.replace(/\}\}\}/g, '</span>');
return rebuild;
}
//处理字符串中可能对正则有影响的字符串
escapeString(value: string): string {
var str = value.replace(new RegExp('\\\\', 'g'), '\\\\');
var characterss = ['(', ')', '[', ']', '{', '}', '^', '$', '|', '?', '*', '+', '.'];
characterss.forEach(function (characters) {
var r = new RegExp('\\' + characters, 'g')
str = str.replace(r, '\\' + characters)
})
return str;
}
/* ---- 生命周期 ---- */
mounted() {
this.highlightKey.sort((a, b) => b.length - a.length);// 为了使高亮正常,关键词长的排在前面
$('#input-textarea').scroll((e) => {
this.setHighlightArea('scrollTop', true);
});
}
}
使用:import HighlightTextarea from "..."引入后,
<highlight-textarea :highlightKey="['=', '<', '>', '!=', '<=', '>=', 'like', '(', ')']" placeholder="请输入监测" :autosize="{minRows: 4, maxRows: 10}" v-model="str"> </highlight-textarea>

浙公网安备 33010602011771号