使用 v-html 指令渲染的标签, 标签内绑定的 click 事件不生效

news2024/10/25 16:57:55

背景

在项目开发中,实现用户友好的输入交互是提升用户体验的关键之一。例如,在客服对话框中,其中有包含多个快捷选项用于快速问答,每个快捷选项都是一个可点击的按钮,并需要绑定点击事件来执行相应操作。然而,直接在 v-html 渲染的标签内绑定的 click 事件不生效。这是因为 v-html 只是将 HTML 字符串插入到 DOM 中,并不会编译其中的 Vue 指令。

v-html 工作原理

  1. 基本用途

    • v-html 指令用于将一个字符串作为 HTML 插入到 DOM 中。

    • 这意味着任何包含在字符串中的 HTML 都会被原样插入,而不会被 Vue 编译。

  2. 工作原理

    • 当 Vue.js 执行 v-html 指令时,它会将字符串解析为 DOM 节点,并将其插入到指定的位置。

    • 这个过程是通过浏览器的 DOM API 完成的,具体来说是通过 innerHTML 属性。

    • 由于插入的是原生的 DOM 节点,而不是经过 Vue 编译的虚拟 DOM,因此其中的 Vue 指令不会被识别和执行。

代码示例

<template>
  <div>
    <el-dialog width="68%" :visible.sync="isShow" :before-close="close" class="kefu" :show-close="false"
      :close-on-click-modal="false">
      <div class="kefu-con">
        <i class="el-icon-close close" @click="close" />
        <div class="header">
          <img :src="kefuImg" alt="">
          <div class="title">小奶龙智能问答助手</div>
        </div>
        <div class="container" ref="container">
          <div class="content" ref="content">
            <div v-for="item in messageForm">
              <div class="reply-container" v-if="item.type === 'reply'">
                <div class="reply-content">
                  <div class="img-con">
                    <img :src="kefuImg" alt="">
                  </div>
                  <div class="reply">
                    <div v-if="item.isXml" v-html="item.content"></div>
                    <p v-else>
                      {{ item.content }}
                    </p>
                  </div>
                </div>
              </div>
              <div class="qs-container" v-if="item.type === 'qs'">
                <div class="qs-content">
                  <div class="qs">
                    <p>
                      {{ item.content }}
                    </p>
                  </div>
                </div>
              </div>
            </div>
          </div>

        </div>
        <div class="footer">
          <img :src="kefuImg" alt="">
          <div class="question-con">
            <el-input class="ipt" v-model="question" placeholder="请输入想咨询的问题"></el-input>
            <el-button class="btn" type="warning" round @click="send">发送</el-button>
          </div>

        </div>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { getAnswer } from '@/api/checkin.js';
export default {
  data() {
    return {
      isShow: false,
      kefuImg: require("@/assets/images/headshot.png"),
      question: '',
      messageForm: [{
        content: "<p>您好, 我是您的小奶龙,你的智能助手。 你可以问我编码相关的问题,也可以一起更高效、更高质量地完成编码工作。比如 “<span class='quick' style='color: #5094d5;cursor: pointer;' @click='quick'>动态路由实现</span>”, “<span class='quick' style='color: #5094d5;cursor: pointer;' @click='quick'>移动端适配</span>” 等等一些问题。</p>",
        type: "reply", // reply 回答  qs 问题
        isXml: true,
      },
      {
        content: '动态路由实现',
        type: "qs", // reply 回答  qs 问题
        isXml: false,
      }
      ]
    }
  },
  methods: {
    quick() {
      console.log('quick方法触发了::: ');
    },
    show() {
      this.isShow = true;
    },
    close() {
      this.isShow = false;
    },
    send() {
      // console.log('this.$refs.content.scrollHeight::: ', this.$refs.content.scrollHeight);

      let NewQuestion = this.question.trim();
      if (NewQuestion === '') {
        return;
      }
      // console.log('this.question::: ', NewQuestion);
      this.messageForm.push({
        content: NewQuestion,
        type: 'qs',
        isXml: false,
      });
     
      setTimeout(()=>{
        // 模拟异步请求
        this.question = '';
      })
    }
  },
}
</script>

页面展示如下,点击快捷问答选项 “动态路由实现” 没有触发 quick 事件
在这里插入图片描述

解决方法

1. 在父容器上监听点击事件,并通过事件对象判断点击的目标元素。

<template>
  <div class="reply" @click="handleProxyClick">
    <div v-html="htmlContent"></div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      htmlContent: '<button class="my-button">点击我</button>'
    };
  },
  methods: {
    handleProxyClick(event) {
      console.log('event::: ', event);
      // 获取触发事件的目标元素  event 事件对象
      const target = event.target;
    },
  }
}
</script>

事件对象里 eventtarget 就是鼠标点击的元素
在这里插入图片描述

2. 如果渲染多个标签,可以通过声明 id 属性或者 class 类名 来判断。

<template>
  <div class="reply" @click="handleProxyClick">
    <div v-html="htmlContent"></div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      htmlContent: '<button id="222" class="my-button">点击我</button>'
    };
  },
  methods: {
    handleProxyClick(event) {
      // 获取触发事件的目标元素  event 事件对象
      const target = event.target;

      console.log("target.classList::: ", target.classList);
      console.log("target.id::: ", target.id);

    },
  }
}
</script>

如图所示
在这里插入图片描述

代码实现

<template>
  <div>
    <el-dialog width="68%" :visible.sync="isShow" :before-close="close" class="kefu" :show-close="false"
      :close-on-click-modal="false">
      <div class="kefu-con">
        <i class="el-icon-close close" @click="close" />
        <div class="header">
          <img :src="kefuImg" alt="">
          <div class="title">小奶龙智能问答助手</div>
        </div>
        <div class="container" ref="container">
          <div class="content" ref="content">
            <div v-for="item in messageForm">
              <div class="reply-container" v-if="item.type === 'reply'">
                <div class="reply-content">
                  <div class="img-con">
                    <img :src="kefuImg" alt="">
                  </div>
                  <div class="reply" @click="handleProxyClick">
                    <div v-if="item.isXml" v-html="item.content"></div>
                    <p v-else>
                      {{ item.content }}
                    </p>
                  </div>
                </div>
              </div>
              <div class="qs-container" v-if="item.type === 'qs'">
                <div class="qs-content">
                  <div class="qs">
                    <p>
                      {{ item.content }}
                    </p>
                  </div>
                </div>
              </div>
            </div>
          </div>

        </div>
        <div class="footer">
          <img :src="kefuImg" alt="">
          <div class="question-con">
            <el-input class="ipt" v-model="question" placeholder="请输入想咨询的问题"></el-input>
            <el-button class="btn" type="warning" round @click="send">发送</el-button>
          </div>

        </div>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { getAnswer } from '@/api/checkin.js';
export default {
  data() {
    return {
      isShow: false,
      kefuImg: require("@/assets/images/headshot.png"),
      question: '',
      messageForm: [{
        content: "<p>您好, 我是您的小奶龙,你的智能助手。 你可以问我编码相关的问题,也可以一起更高效、更高质量地完成编码工作。比如 “<span class='quick' style='color: #5094d5;cursor: pointer;' @click='quick'>动态路由实现</span>”, “<span class='quick' style='color: #5094d5;cursor: pointer;' @click='quick'>移动端适配</span>” 等等一些问题。</p>",
        type: "reply", // reply 回答  qs 问题
        isXml: true,
      },
      {
        content: '动态路由实现',
        type: "qs", // reply 回答  qs 问题
        isXml: false,
      }
      ]
    }
  },
  methods: {
    handleProxyClick(event) {
      // 获取触发事件的目标元素  event 事件对象
      const target = event.target;
      // 判断目标元素是否包含指定类名
      if (target.classList.contains('quick')) {
        // 传递目标元素的文本内容
        this.quick(target.outerText);
      }
    },
    quick(text) {
      console.log('quick方法触发了::: ');
      this.question = text;
      // 发送
      this.send();
    },
    show() {
      this.isShow = true;
    },
    close() {
      this.isShow = false;
    },
    send() {
      let NewQuestion = this.question.trim();
      if (NewQuestion === '') {
        return;
      }
      this.messageForm.push({
        content: NewQuestion,
        type: 'qs',
        isXml: false,
      });
      setTimeout(() => {
        // 模拟异步请求
        this.question = '';
      })
    }
  },
}
</script>

实现效果
在这里插入图片描述

总结

在项目开发中,某些对话框中的快捷选项使用 v-html 渲染,导致标签内绑定的 click 事件不生效。为了解决这一问题,可以通过在父容器上使用事件代理(如 @click 事件监听器),并通过事件对象判断点击的目标元素,从而调用相应的处理方法,确保点击事件能够正常触发。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2223420.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android Junit 单元测试 | 依赖配置和编译报错解决

问题 为什么在依赖中添加了testImplement在build APK的时候还是会报错&#xff1f;是因为没有识别到test文件夹是test源代码路径吗&#xff1f; 最常见的配置有: implementation - 所有源代码集(包括test源代码集)中都有该依赖库.testImplementation - 依赖关系仅在test源代码…

【CSS in Depth 2 精译_054】8.2 CSS 层叠图层(cascade layer)的推荐组织方案

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 【第三部分 现代 CSS 代码组织】 ✔️【第八章 层叠图层及其嵌套】 ✔️ 8.1 用 layer 图层来操控层叠规则&#xff08;上篇&#xff09; 8.1.1 图层的定义&#xff08;上篇&#xff09;8.1.2 图层的…

华为云实战杂记

配置nginx服务器 首先我们拿到一台服务器时&#xff0c;并不知道系统是否存在Nginx我们可以在Linux命令行执行如下命令查看 find / -name nginx* find / -name nginx* 查找所有名字以nginx开头的文件或者目录&#xff0c;我们看看系统里面都有哪些文件先&#xff0c;这样可以快…

Linux系统安装Redis详细操作步骤(二进制发布包安装方式)

安装方式介绍 在Linux系统中&#xff0c;安装软件的方式主要有四种&#xff0c;这四种安装方式的特点如下&#xff1a; 安装方式特点二进制发布包安装软件已经针对具体平台编译打包发布&#xff0c;只要解压&#xff0c;修改配置即可rpm安装软件已经按照redhat的包管理规范进…

雷池社区版有多个防护站点监听在同一个端口上,匹配顺序是怎么样的

如果域名处填写的分别为 IP 与域名&#xff0c;那么当使用进行 IP 请求时&#xff0c;则将会命中第一个配置的站点 以上图为例&#xff0c;如果用户使用 IP 访问&#xff0c;命中 example.com。 如果域名处填写的分别为域名与泛域名&#xff0c;除非准确命中域名&#xff0c;否…

NGINX 保护 Web 应用安全之基于 IP 地址的访问

根据客户端的 IP 地址控制访问 使用 HTTP 或 stream 访问模块控制对受保护资源的访问&#xff1a; location /admin/ { deny 10.0.0.1; allow 10.0.0.0/20; allow 2001:0db8::/32; deny all; } } 给定的 location 代码块允许来自 10.0.0.0/20 中的任何 IPv4 地址访问&#xf…

UE4_Niagara基础实例—9、使用条带渲染器来制作闪电

效果图&#xff1a; 一、通过模板Static Beam来熟悉条带渲染器 从Static Beam发射器新建niagara系统&#xff0c;更名为NS_StaticBeam。 打开粒子系统&#xff0c;界面如下&#xff1a; Beam Emitter Setup模块可以设置条带的开始点、结束点和切线。 我们就可以通过这个Beam E…

自动化测试:等待方式

在自动化测试中&#xff0c;等待是一个重要的技术&#xff0c;用于处理页面加载、元素定位、元素状态改变等延迟问题。 等待能够确保在条件满足后再进行后续操作&#xff0c;提高自动化测试的稳定性以及可靠性。 等待方式&#xff1a;显示等待、隐式等待、线程睡眠 1. 显式等…

【python】OpenCV—WaterShed Algorithm(1)

文章目录 1、功能描述2、代码实现3、完整代码4、效果展示5、涉及到的库函数5.1、cv2.pyrMeanShiftFiltering5.2、cv2.morphologyEx5.3、cv2.distanceTransform5.4、cv2.normalize5.5、cv2.watershed 6、参考 1、功能描述 基于分水岭算法对图片进行分割 分水岭分割算法&#x…

什么是域名?什么是泛域名?

域名 定义 域名是互联网上用于识别和定位网站或网络服务的名称。它是由一串用点分隔的字符组成&#xff0c;例如 “baidu.com”。就像是现实生活中建筑物的地址&#xff0c;方便用户在互联网的海量信息中找到特定的网站。 结构 域名从右到左依次为顶级域名&#xff08;TLD&…

go语言中的Scan()和Scanln()输入函数

Scan()输入函数 package mainimport "fmt"func main() {var a intvar b stringfor {fmt.Println("请输入一个整数和一个字符串&#xff08;用空格分隔&#xff09;&#xff1a;")fmt.Scan(&a, &b) // 直接读取输入到变量中fmt.Println("整数…

图书管理系统的简单实现

文章目录 图书系统逻辑分析各种操作功能的实现完整代码 个人主页 JavaSE专栏 图书系统逻辑分析 该程序设置有三个包&#xff0c;user 包&#xff0c;book 包&#xff0c;operation包。 book包中包含对于书的一些信息和操作;operation包中包含有对 书 的所有操作功能;user包中包…

‘perl‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。

‘perl’ 不是内部或外部命令,也不是可运行的程序 或批处理文件。 明明已经根据教程安装了perl环境,但是在cmd中依赖报该错误,本章教程提供解决办法。 一、激活perl环境 state shell ActiveState-Perl-5.36.0此时输入perl -v 是可以直接输出perl版本号的。 二、找到perl的执…

想进体制内?到底有哪些路可走?原来有这么多方法

在如今的就业大环境下&#xff0c;体制内工作越来越受到大家的青睐。那么&#xff0c;体制内为何如此受欢迎呢&#xff1f; 一、体制内为何备受青睐 体制内工作首先给人一种强烈的稳定感和安全感。一旦进入体制&#xff0c;你不用时刻担心失业的风险&#xff0c;能够拥有一份长…

安康旅游网站:SpringBoot设计与实现详解

目 录 目 录 I 摘 要 III Abstract IV 第一章 绪论 1 1.1 研究现状 1 1.2 设计原则 1 1.3 研究内容 2 第二章 相关技术简介 1 2.1 JSP技术 1 2.2 Java技术 2 2.3 MYSQL数据库 2 2.4 B/S结构 3 2.5 Spring Boot框架 4 第三章 系统分析 5 3.1可行性分析 5 3.1.1技术可行性 5 3.1.…

阿里云项目启动OOM问题解决

问题描述 随着项目业务的增长&#xff0c;系统启动时内存紧张&#xff0c;每次第一次启动的时候就会出现oom第二次或者第n的时候&#xff0c;就启动成功了。 带着这个疑问&#xff0c;我就在阿里云上提交了工单&#xff0c;咨询为什么第一次提交失败但是后面却能提交成功尼&a…

开挖 Domain - 前奏

WPF App 主机配置 Microsot.Extension.Hosting 一键启动&#xff08;配置文件、依赖注入&#xff0c;日志&#xff09; // App.xaml.cs 中定义 IHost private readonly IHost _host Host.CreateDefaultBuilder().ConfigureAppConfiguration(c > {_ c.SetBasePath(Envi…

基于NERF技术重建学习笔记

NeRF&#xff08;Neural Radiance Fields&#xff09;是一种用于3D场景重建的神经网络模型&#xff0c;能够从2D图像生成逼真的3D渲染效果。它将场景表征为一个连续的5D函数&#xff0c;利用了体积渲染和神经网络的结合&#xff0c;通过学习光线穿过空间时的颜色和密度来重建场…

邮件营销的目的详解:促进销售与业绩增长!

邮件营销的目的效果评估&#xff1f;发不同类型营销邮件的目的&#xff1f; 邮件营销作为一种高效且成本低廉的营销手段&#xff0c;被广泛应用于各行各业。邮件营销的目的不仅仅是简单的信息传递&#xff0c;更是为了促进销售与业绩增长。MailBing将深入探讨邮件营销的目的&a…

01 springboot-整合日志(logback-config.xml)

logback-config.xml 是一个用于配置 Logback 日志框架的 XML 文件&#xff0c;通常位于项目的 classpath 下的根目录或者 src/main/resources 目录下。 Logback 提供了丰富的配置选项&#xff0c;可以满足各种不同的日志需求。需要根据具体情况进行配置。 项目创建&#xff0…