feat: v-html加载dom动态添加事件,埋点等

master
LCJ-MinYa 11 months ago
parent c1f98b5c16
commit 8adb8fd3bd

@ -44,6 +44,10 @@ const titleArr = [
key: 'autoLoadData', key: 'autoLoadData',
title: '首页高度足够,自动触发下拉加载数据', title: '首页高度足够,自动触发下拉加载数据',
}, },
{
key: 'dynamicNodeEvent',
title: '动态html加载的事件监听',
},
]; ];
// @/views/demo/**/*.vue // @/views/demo/**/*.vue

@ -0,0 +1,222 @@
<template>
<div
ref="boxRef"
class="box"
@scroll.passive="handleScroll"
>
<div v-loading="loading">
<div
v-for="(item, index) in list"
:key="index"
class="html-item"
>
<h2>标题{{ item.title }}</h2>
<div
v-html="item.html"
@click="handleClickHtml($event, item)"
></div>
<el-divider />
</div>
</div>
</div>
</template>
<script setup lang="jsx">
import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue';
import { debounce } from '@pureadmin/utils';
const boxRef = ref(null);
const list = ref([]);
const loading = ref(false);
const videoAddEventListeners = ref([]);
/**
* 将富文本内的所有点击链接点击图片点击视频点击按钮等绑定埋点事件
*/
const html = `<a href='https://www.baidu.com' style='text-decoration: underline;'>点击链接</a><br /><br/>
<img src='https://www.baidu.com/img/flexible/logo/pc/result.png' /><br /><br/>
<button style='background-color: #4CAF50; border: none; color: white; padding: 10px 20px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; margin: 4px 2px; cursor: pointer;'>按钮</button>
<video src="https://www.w3school.com.cn/i/movie.ogg" controls="controls"></video>
<video controls="controls"><source src="https://www.w3school.com.cn/i/movie.mp4" type="video/ogg"></video>
<a href='https://www.baidu.com' style='text-decoration: underline;'><span>有内容标签的链接</span></a><br /><br/>
<a href='https://www.baidu.com' style='text-decoration: underline;'><img alt='可以跳转的图片' src='https://www.baidu.com/img/flexible/logo/pc/result.png' /></a><br /><br/>
`;
const getHerfAttribute = (dom) => {
return dom.getAttribute('href') || dom.href || '';
};
// v-html
const handleClickHtml = async (event, item) => {
const currentDom = event.target;
const parentDom = currentDom.parentNode;
let currentDomHref = getHerfAttribute(currentDom);
let parentHref = getHerfAttribute(parentDom);
//
let jumpLink = '';
//
let params = {};
switch (currentDom.tagName) {
case 'A':
event.preventDefault();
jumpLink = currentDomHref;
params.link = currentDomHref;
params.type = 'link';
break;
case 'IMG':
if (parentDom.tagName === 'A') {
event.preventDefault();
jumpLink = parentHref;
params.link = parentHref;
}
params.type = 'image';
params.src = currentDom.getAttribute('src') || currentDom.src;
break;
case 'BUTTON':
params.type = 'button';
params.text = currentDom.textContent;
break;
default:
if (parentDom.tagName === 'A') {
event.preventDefault();
jumpLink = parentHref;
params.link = parentHref;
params.type = 'link';
}
break;
}
//
if (params.type) {
await new Promise((resolve) => {
params.title = item.title;
setTimeout(() => {
console.log(`触发${params.type}埋点事件`);
console.table(params);
resolve();
}, 500);
});
//
if (jumpLink) {
console.log(`跳转链接:${jumpLink}`);
// window.location.href = jumpLink;
}
}
};
const videoBuryingPoint = (type, src, title) => {
console.log(`视频${type}事件,视频地址:${src}, 视频所属标题:${title}`);
};
const videoAddEventListener = () => {
let videos = boxRef.value.querySelectorAll('video');
for (let i = 0; i < videos.length; i++) {
const video = videos[i];
//
if (!video.dataset.listenerAdded) {
//video srcvideo.src
const src = video.src;
//video sourcesourcesrc
const sources = video.getElementsByTagName('source');
//sourcesourcesrc
const sourceSrc = sources.length ? sources[0].src : '';
// 'html-item'
const ancestorWithHtmlItemClass = video.closest('.html-item');
const title = ancestorWithHtmlItemClass.querySelector('h2').textContent;
const playHandler = () => videoBuryingPoint('play', src || sourceSrc, title);
const pauseHandler = () => videoBuryingPoint('pause', src || sourceSrc, title);
const endedHandler = () => videoBuryingPoint('ended', src || sourceSrc, title);
const volumeChangeHandler = () => videoBuryingPoint('volumechange', src || sourceSrc, title);
video.addEventListener('play', playHandler);
video.addEventListener('pause', pauseHandler);
video.addEventListener('ended', endedHandler);
video.addEventListener('volumechange', volumeChangeHandler);
// 便
videoAddEventListeners.value.push({
video,
playHandler,
pauseHandler,
endedHandler,
volumeChangeHandler,
});
video.dataset.listenerAdded = true;
}
}
};
const videoRemoveEventListener = () => {
videoAddEventListeners.value.forEach((item) => {
const { video, playHandler, pauseHandler, endedHandler, volumeChangeHandler } = item;
video.removeEventListener('play', playHandler);
video.removeEventListener('pause', pauseHandler);
video.removeEventListener('ended', endedHandler);
video.removeEventListener('volumechange', volumeChangeHandler);
});
videoAddEventListeners.value = [];
};
const getData = () => {
loading.value = true;
console.log('加载数据');
setTimeout(() => {
loading.value = false;
let tempList = [];
let currentLength = list.value.length;
for (let i = 0; i < 5; i++) {
tempList.push({
title: `模拟数据${i + currentLength}`,
html,
});
}
list.value = list.value.concat(tempList);
nextTick(() => {
videoAddEventListener();
});
}, 2000);
};
const scrollLoadData = () => {
console.log('监听滚动事件');
/**
* scrollTop滚动元素的顶部到可视区域顶部的距离
* clientHeight可视区域的高度
* scrollHeight内容的总高度
*/
const { scrollTop, clientHeight, scrollHeight } = boxRef.value;
console.log(scrollTop, clientHeight, scrollHeight);
if (scrollTop + clientHeight >= scrollHeight - 20) {
console.log('距离底部还有20px, 加载数据');
getData();
}
};
const handleScroll = debounce(scrollLoadData, 500);
onMounted(() => {
getData();
});
onBeforeUnmount(() => {
videoRemoveEventListener();
});
</script>
<style lang="scss" scoped>
.box {
display: flex;
width: 100%;
flex-direction: column;
align-items: center;
background: #fff;
// auto
height: calc(100vh - 81px);
overflow-y: auto;
& > div {
width: 100%;
padding: 10px 20px;
}
}
</style>
Loading…
Cancel
Save