• 需求描述:

    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('没有选中任何关键字');  
        }  
    };
    
  • 总结:

    这样就实现了查询、高亮、替换等功能。
    如果大家对我的代码有优化建议,可以评论随时交流~

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐