From 98fac39e557549fdc3dfde6125f0bce65975683f Mon Sep 17 00:00:00 2001 From: chirokas <157580465+chirokas@users.noreply.github.com> Date: Tue, 20 Feb 2024 17:17:52 +0800 Subject: [PATCH] feat(useMutationObserver): allow multiple targets (#3741) --- .../core/useMutationObserver/index.test.ts | 31 +++++++++++++++++++ packages/core/useMutationObserver/index.ts | 27 ++++++++++------ 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/packages/core/useMutationObserver/index.test.ts b/packages/core/useMutationObserver/index.test.ts index 9cc75ca5570..f7c58b5796c 100644 --- a/packages/core/useMutationObserver/index.test.ts +++ b/packages/core/useMutationObserver/index.test.ts @@ -1,5 +1,6 @@ import { promiseTimeout } from '@vueuse/shared' import { describe, expect, it, vi } from 'vitest' +import { computed, ref } from 'vue-demi' import { useMutationObserver } from '.' describe('useMutationObserver', () => { @@ -187,4 +188,34 @@ describe('useMutationObserver', () => { expect(records![0].target).toBe(target) expect(cb).toHaveBeenCalledTimes(1) }) + + it('should work with multiple targets', async () => { + const headerElement = ref( + document.createElement('div'), + ) + const footerElement = ref( + document.createElement('div'), + ) + const targets = computed(() => [headerElement.value, footerElement.value]) + const cb = vi.fn() + + const { takeRecords } = useMutationObserver(targets, cb, { + attributes: true, + }) + + headerElement.value?.setAttribute('id', 'header') + footerElement.value?.setAttribute('id', 'footer') + let records = takeRecords() + await promiseTimeout(10) + expect(records).toHaveLength(2) + expect(records![0].target).toBe(headerElement.value) + expect(records![1].target).toBe(footerElement.value) + + headerElement.value = null + footerElement.value?.removeAttribute('id') + records = takeRecords() + await promiseTimeout(10) + expect(records).toHaveLength(1) + expect(records![0].target).toBe(footerElement.value) + }) }) diff --git a/packages/core/useMutationObserver/index.ts b/packages/core/useMutationObserver/index.ts index f17a01db1ca..9b4138429f3 100644 --- a/packages/core/useMutationObserver/index.ts +++ b/packages/core/useMutationObserver/index.ts @@ -1,6 +1,7 @@ -import { tryOnScopeDispose } from '@vueuse/shared' -import { watch } from 'vue-demi' -import type { MaybeComputedElementRef } from '../unrefElement' +import type { MaybeRefOrGetter } from '@vueuse/shared' +import { notNullish, toValue, tryOnScopeDispose } from '@vueuse/shared' +import { computed, watch } from 'vue-demi' +import type { MaybeComputedElementRef, MaybeElement } from '../unrefElement' import { unrefElement } from '../unrefElement' import { useSupported } from '../useSupported' import type { ConfigurableWindow } from '../_configurable' @@ -18,7 +19,7 @@ export interface UseMutationObserverOptions extends MutationObserverInit, Config * @param options */ export function useMutationObserver( - target: MaybeComputedElementRef, + target: MaybeComputedElementRef | MaybeComputedElementRef[] | MaybeRefOrGetter, callback: MutationCallback, options: UseMutationObserverOptions = {}, ) { @@ -33,17 +34,25 @@ export function useMutationObserver( } } + const targets = computed(() => { + const value = toValue(target) + const items = (Array.isArray(value) ? value : [value]) + .map(unrefElement) + .filter(notNullish) + return new Set(items) + }) + const stopWatch = watch( - () => unrefElement(target), - (el) => { + () => targets.value, + (targets) => { cleanup() - if (isSupported.value && window && el) { + if (isSupported.value && window && targets.size) { observer = new MutationObserver(callback) - observer!.observe(el, mutationOptions) + targets.forEach(el => observer!.observe(el, mutationOptions)) } }, - { immediate: true }, + { immediate: true, flush: 'post' }, ) const takeRecords = () => {