一、场景
- 背景:因为系统里经常有新活动或者公告需要通知所有用户,希望前端维护的这个弹窗里的内容可以由后端接口返回。这样就不需要每次上新活动的时候,前端项目都发版了。因此,前端维护了这个弹窗和它的关闭事件,至于弹窗里展示什么内容,则由接口返回。
- 问题:后端返回了展示的HTML里有一个按钮,希望点击这个按钮时可以关闭弹窗。如下图的交叉按钮:
二、解决方案
- 考虑到需求的历史背景,以及后续的可维护性。决定在后端返回的HTML里,给期望关闭弹窗的按钮添加上ID。
- 而在前端项目里,弹窗加载完成并且接口已经返回数据后,我们可以通过ID获取到用于关闭弹窗的按钮元素,然后给这个按钮添加一个点击事件监听器。当用户点击该元素时,会触发关闭弹窗的函数。
三、代码演示
Step1. 前端项目里存放着弹窗,弹窗里的内容通过读取接口返回的 html 赋值给content
,然后通过v-html
呈现。将关闭弹窗的函数写在此处,下面仅展示一些核心的实现代码(Vue3+Vuetify):
<template>
<v-dialog
v-model="visible"
>
<v-card >
<div v-html="content">
</div>
</v-card>
</v-dialog>
</template>
handleClose() {
this.visible = false
}
Step2. 此时,接口返回一段JSON,用于页面呈现。里面可能包含了一个按钮用于关闭这个弹窗,我们需要给这个按钮添加上ID(下图里的id=\"close-btn\"
),与后续的步骤关联。
{
"content": "<div>\n <a type=\"button\">\n <span class=\"v-btn__content\" id=\"close-btn\">关闭</span>\n </a>\n </div>",
"startTime": "2023-12-01 00:00:00",
"endTime": "2024-2-29 00:00:00"
}
Step3. 在前端项目里,弹窗加载完成并且接口已经返回数据后,我们可以通过ID获取到用于关闭弹窗的按钮元素,然后给这个按钮添加一个点击事件监听器。当用户点击该元素时,会触发关闭弹窗的函数。
document.getElementById('close-btn')?.addEventListener('click', this.handleClose)
四、可能存在的坑点
需要等接口的数据返回并且生成了相关的元素以后,才能对按钮添加事件监听。否则会监听不到这个元素。验证过程如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>验证节点未生成时添加事件监听是否有效</title>
<!-- 这个案例证明了给一个不存在的节点(通过指定ID的方式)添加事件监听是无效的,即使之后会往页面上生成同样id的元素 -->
<!-- 因此,一定要确保元素已经在页面上挂载了,才去添加事件监听 -->
</head>
<body>
<div id="parent">
<button
id="create"
>
生成新按钮
</button>
</div>
<script>
/**
* 创造一个新的元素
**/
function createNewNode() {
const parentElem = document.getElementById('parent')
const newElem = document.createElement('button')
newElem.textContent = '新按钮'
newElem.setAttribute('id', 'new')
parentElem?.insertBefore(newElem, document.getElementById('create'))
document.getElementById('new')?.addEventListener('click', function () {
newNodeOnClick('按钮生成后')
})
}
/**
* 新元素被点击时触发
* @param timing 点击的时机
**/
function newNodeOnClick(timing) {
console.log('newNodeOnClick', timing)
}
document.addEventListener('DOMContentLoaded', function (e) {
document.getElementById('create')?.addEventListener('click', createNewNode)
document.getElementById('new')?.addEventListener('click', function () {
newNodeOnClick('页面加载完毕后')
})
})
</script>
</body>
</html>