使用自定义指令实现高亮
原创2024年7月5日大约 2 分钟
需求
需求是: 在前端页面有很多文本,要求提取文本,根据规则判断文本是否和规则互斥,比如“女”和“前列腺”就是互斥的。当互斥时,需要高亮,同时当鼠标 指在文本上,弹出提示框,提示“女和前列腺规则互斥” 在页面中,通过自定义指令,实现对文本的局部高亮,其中高亮内容是通过接口动态获取。
实现方案
利用vue的自定义指令,实现局部高亮。组件调用自定义指令,同时传入高亮内容,指令内部根据传入的高亮内容,对文本进行局部高亮。
代码
export default {
mounted(el, binding) {
const applyHighlight = () => {
const wordsToHighlight = binding.value.words
const highlightColor = binding.value.color || '#e8b61d'
function highlightText(node: any) {
if (node.nodeType === 3) {
// 文本节点
let text = node.nodeValue
wordsToHighlight.forEach((word: string) => {
const regex = new RegExp(`(${word})`, 'gi')
// 替换文本,在需要高亮的文本前添加背景色高亮
text = text.replace(
regex,
`<span style="background-color: ${highlightColor};cursor: pointer;" class="hightLightText" >$1</span>`,
)
})
const tempDiv = document.createElement('div')
tempDiv.innerHTML = text
while (tempDiv.firstChild) {
node.parentNode.insertBefore(tempDiv.firstChild, node)
}
node.parentNode.removeChild(node)
} else if (
node.nodeType === 1 &&
node.childNodes &&
!/(script|style)/i.test(node.tagName)
) {
for (let i = 0; i < node.childNodes.length; i++) {
highlightText(node.childNodes[i])
}
}
}
highlightText(el)
}
// 初次高亮
applyHighlight()
// 在指令对象上添加 applyHighlight 方法
el.__vueApplyHighlight = applyHighlight
},
updated(el) {
if (el.__vueApplyHighlight) {
el.__vueApplyHighlight()
}
},
unmounted(el) {
if (el.__vueApplyHighlight) {
delete el.__vueApplyHighlight
}
},
}
在组件中,调用自定义指令,同时传入高亮内容,大概如下:
<ElTabPane
v-highlight-text="{
words: highLightWords,
}"
@mouseover="handleHightLightText"
label="文本报告"
name="textReport"></ElTabPane>
扩展
以上只实现了高亮,还需要实现鼠标悬浮自动提示规则互斥的文本。
<el-tooltip
v-model:visible="wordsTooltip.visible"
:content="wordsTooltip.content"
placement="top"
effect="dark"
virtual-triggering
:raw-content="true"
:virtual-ref="wordsTooltipRef"></el-tooltip>
写一个el-tooltip组件,默认隐藏,当鼠标悬浮在文本上时,显示提示框,再提取高亮文本,当成key,根据key从一个map获取规则互斥内容。
const wordsTooltip = reactive({
visible:false,
content: '',
position: {
top: 0,
left: 0,
bottom: 0,
right: 0,
}
})
const wordsTooltipRef = ref({
getBoundingClientRect() {
return wordsTooltip.position
},
})
const handleHightLightText = (event: MouseEvent) => {
wordsTooltip.visible = false
const target = event.target as HTMLElement
let className = target?.className
let nodeName = target?.nodeName
if (className == 'hightLightText' && nodeName == 'SPAN') {
const text = target.innerText
const filterWords = ruleWords.value.filter(v => {
return v.rule.find(v=>v.includes(text))
})
if(filterWords.length){
wordsTooltip.visible = true
const rect = target.getBoundingClientRect()
wordsTooltip.content = filterWords.map(v=>v.desc).join('<br />')
wordsTooltip.position = DOMRect.fromRect({
width: 0,
height: 0,
x: rect.left+(rect.width/2),
y: rect.top,
})
}
}else{
wordsTooltip.visible = false
}
}
参考elementui的tooltip组件,虚拟触发~