如何在计时器对应的HTMLDivElement存在时执行操作?
跟踪Timer元素存活状态的解决方案
问题背景
自定义了一个Timer类,会返回用于倒计时至0的HTMLDivElement,代码示例及使用方式如下:
/* Example: Create timer let timer = new Timer('div', 'timer', timeForGame).render(); Timer fires event when clock id down to 0 timer.addEventListener('countDown', () => { console.log("Timer is out") }) */ export default class Timer extends Component{ protected time: number; protected event: CustomEvent; constructor(tagName: string, className: string, time: number) { super(tagName, className); this.time = time; this.event = new CustomEvent("countDown") } startTimer(){ let countdown = this.time * 1000; this.container.textContent = String(countdown / 1000); const countingDown = () => { console.log(countdown / 1000, " seconds"); countdown -= 1000; this.container.textContent = String(countdown / 1000); if (countdown === 0){ this.container.dispatchEvent(this.event); clearInterval(Timer) } } const Timer = setInterval(countingDown, 1000); } render() { this.container.innerHTML = String(this.time); this.startTimer(); return this.container; } }
尝试用以下代码跟踪元素存活状态但未生效:
while(document.querySelector('.timer') !== null){ setTimeout(() => { console.log('i exist') }, 0); }
问题原因
你写的while循环是同步死循环,JavaScript是单线程模型,这个循环会持续占用主线程,导致setTimeout的回调永远无法执行,页面也会直接卡死。而且循环是同步执行的,不会主动重新查询DOM状态,即使元素被移除,循环也不会停止。
解决方案
方法1:复用现有countDown事件(跟踪逻辑存活状态)
如果你的"存活状态"指的是倒计时未结束,直接复用Timer类已有的countDown事件即可,不需要额外轮询:
let timer = new Timer('div', 'timer', timeForGame).render(); // 监听倒计时结束事件,此时Timer逻辑上不再"存活" timer.addEventListener('countDown', () => { console.log('Timer 倒计时结束'); });
方法2:用MutationObserver跟踪DOM中的物理存活状态
如果需要跟踪元素是否被从DOM中移除(物理上的存活状态),使用浏览器原生的MutationObserver监听DOM变化:
// 创建Timer实例并添加到DOM let timer = new Timer('div', 'timer', 5).render(); document.body.appendChild(timer); // 创建MutationObserver实例 const observer = new MutationObserver((mutations) => { mutations.forEach(mutation => { // 检查本次变更中是否包含被移除的Timer元素 const isTimerRemoved = Array.from(mutation.removedNodes).includes(timer); if (isTimerRemoved) { console.log('Timer元素已从DOM中移除'); observer.disconnect(); // 完成跟踪后停止监听,节省资源 } }); }); // 配置监听选项:监听body及其子树的子节点变化 observer.observe(document.body, { childList: true, subtree: true });
方法3:扩展Timer类,新增自定义移除事件(可选)
如果需要更灵活的控制权,可以在Timer类内部集成监听逻辑,新增元素移除时的自定义事件:
export default class Timer extends Component{ protected time: number; protected countDownEvent: CustomEvent; protected removedEvent: CustomEvent; constructor(tagName: string, className: string, time: number) { super(tagName, className); this.time = time; this.countDownEvent = new CustomEvent("countDown"); this.removedEvent = new CustomEvent("timerRemoved"); // 监听元素是否被移除出DOM const elementObserver = new MutationObserver(() => { if (!document.contains(this.container)) { this.container.dispatchEvent(this.removedEvent); elementObserver.disconnect(); } }); elementObserver.observe(document.body, { childList: true, subtree: true }); } startTimer(){ let countdown = this.time * 1000; this.container.textContent = String(countdown / 1000); const countingDown = () => { console.log(countdown / 1000, " seconds"); countdown -= 1000; this.container.textContent = String(countdown / 1000); if (countdown === 0){ this.container.dispatchEvent(this.countDownEvent); clearInterval(timerInterval) } } const timerInterval = setInterval(countingDown, 1000); } render() { this.container.innerHTML = String(this.time); this.startTimer(); return this.container; } } // 使用方式 let timer = new Timer('div', 'timer', 5).render(); document.body.appendChild(timer); timer.addEventListener('timerRemoved', () => { console.log('Timer元素已被移除'); });
内容的提问来源于stack exchange,提问作者Danyil Derkach
相关产品推荐
相关产品推荐

