vue3实现搜索关键字高亮、查找、替换等功能
·
-
需求描述:
1)页面中存在多个文本框
2)点击查找替换按钮弹出查找替换框
3)当用户输入内容后,实现根据输入框对文本框的内容进行查找、高亮,同时可替换、查找上一处、下一处等功能。 -
实现效果:
1)获取搜索关键字,并过滤出符合条件的数据
2)匹配出要高亮的关键字
3)增加样式 -
相关代码:
1)如何实现文本框既支持输入、同时又能标注高亮呢?普通的textarea是实现不了的,于是采用给div添加contenteditable,同时监听blur和focus事件
<div ref="editableDiv" :contenteditable="!readonly" @blur="contentBlur(index,$event.target.innerText)" @focus="contentInput(index,$event.target.innerText)" class="editable content-detail" v-html="item.contentDetail" >2)关键点就在于怎么匹配,如何高亮?我采用的是正则匹配,再用replace替换
- 查找替换弹出框代码:
<el-form style="margin-top: 30px;"> <el-form-item label="查找内容"> <el-input placeholder="请输入查找内容" clearable v-model="keyWordInput" @input="keyWordChange" @keyup.down.native="nextKeyWord" @keyup.up.native="preKeyWord" @keyup.enter.native="nextKeyWord"> </el-input> </el-form-item> <el-form-item label="替换内容"> <el-input v-model="replaceContent" placeholder="请输入替换内容" clearable></el-input> </el-form-item> </el-form> <div class="form-btns flex-box"> <template v-for="(btnItem,index) in optionBtnList" :key="index"> <el-button type="primary" @click="() => btnItem.operateFn()"> {{ btnItem.label }} </el-button> </template> </div>- 具体实现方法:
// 文本输入框blur事件 const contentBlur = async (index, newContent) => { props.scriptList[index].contentDetail = newContent; if(keyWordInput.value){ await locationLightKeyWord(); } } const contentInput = async (index, val) => { currentNodeIndex.value = 0 await removeHighlights(); } // 查找输入框change事件 const keyWordChange = async () => { currentNodeIndex.value = 0; await removeHighlights(); if(keyWordInput.value){ await locationLightKeyWord(); nowNum.value = 1; }else{ nowNum.value = 0; totalNum.value = 0; await removeHighlights(); } } // 高亮匹配文字 const locationLightKeyWord = async () => { await brightenKeyword(); if(allLightKeyWords.value.length > 0){ totalNum.value = allLightKeyWords.value.length; allLightKeyWords.value[currentNodeIndex.value].className = 'lightStyle currentLightStyle'; } } // 替换高亮文字文本 const brightenKeyword = async () => { allLightKeyWords.value = []; const Reg = new RegExp(keyWordInput.value, 'ig'); // 遍历文本内容替换高亮关键字 props.scriptList.forEach((item, index) => { item.contentDetail = item.contentDetail.replace(Reg, '<span class="lightStyle">' + keyWordInput.value + '</span>'); }); allLightKeyWords.value = document.getElementsByClassName('lightStyle'); return allLightKeyWords.value; } // 清除之前匹配到的高亮关键字 const removeHighlights = async () => { props.scriptList.forEach(item => { const parser = new DOMParser(); const doc = parser.parseFromString(item.contentDetail, 'text/html'); function cleanSpans(node) { if (node.nodeType === Node.ELEMENT_NODE) { if (node.tagName === 'SPAN' && (node.classList.contains('lightStyle') || node.classList.contains('currentLightStyle'))) { const text = node.textContent || ''; const parent = node.parentNode; if (parent) { parent.replaceChild(document.createTextNode(text), node); } } node.childNodes.forEach(child => cleanSpans(child)); } } cleanSpans(doc.body); item.contentDetail = doc.body.innerHTML; }); } // 切换查找上一个 const preKeyWord = async () => { if (totalNum.value < 1) { ElMessage.info('没有上一处'); return; } if (currentNodeIndex.value === 0) { ElMessage.info('已经是第一个'); return; } currentNodeIndex.value = currentNodeIndex.value - 1; nowNum.value = currentNodeIndex.value + 1; await changeCurrentKeyword(); } // 切换查找下一个 const nextKeyWord = async () => { if (totalNum.value < 1) { ElMessage.info('没有下一处'); return; } if (currentNodeIndex.value === totalNum.value - 1) { ElMessage.info('已经是最后一个'); return; } else { currentNodeIndex.value = currentNodeIndex.value + 1; } nowNum.value = currentNodeIndex.value + 1; await changeCurrentKeyword(); } // 切换当前高亮关键字 const changeCurrentKeyword = async () =>{ allLightKeyWords.value[currentNodeIndex.value].className = 'lightStyle currentLightStyle'; if(allLightKeyWords.value[currentNodeIndex.value - 1]){ allLightKeyWords.value[currentNodeIndex.value - 1].className = 'lightStyle' } if(allLightKeyWords.value[currentNodeIndex.value + 1]){ allLightKeyWords.value[currentNodeIndex.value + 1].className = 'lightStyle' } // 滚动到当前高亮关键字位置 window.scrollTo({ top: document.querySelector('.currentLightStyle').getBoundingClientRect().top + window.pageYOffset - 100, behavior: 'smooth' // 可选,实现平滑滚动 }); } // 全部替换当前关键字 const replaceAll = async () => { const replaceText = replaceContent.value; const Reg = new RegExp(keyWordInput.value, 'ig'); props.scriptList.forEach(item => { item.contentDetail = item.contentDetail.replace(Reg, replaceText); }); // 清除高亮并重新高亮 await removeHighlights(); } // 替换当前关键字 const replaceCurrent = async () => { if (totalNum.value > 0 && currentNodeIndex.value >= 0 && currentNodeIndex.value < totalNum.value) { // 获取当前高亮的关键字元素 const currentHighlightElement = document.querySelectorAll('.lightStyle')[currentNodeIndex.value]; if (currentHighlightElement) { const replaceText = replaceContent.value; const originalText = currentHighlightElement.textContent; currentHighlightElement.textContent = replaceText; currentHighlightElement.classList.remove('currentLightStyle'); currentHighlightElement.classList.remove('lightStyle'); allLightKeyWords.value[currentNodeIndex.value].className = 'lightStyle currentLightStyle'; } } else { ElMessage.error('没有选中任何关键字'); } }; -
总结:
这样就实现了查询、高亮、替换等功能。
如果大家对我的代码有优化建议,可以评论随时交流~
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)