Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(useActiveElement): optionally ignore active <body> element #2591

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 16 additions & 3 deletions packages/core/useActiveElement/index.ts
@@ -1,16 +1,21 @@
import type { MaybeRef } from '@vueuse/shared'
import { computedWithControl } from '@vueuse/shared'
import { useEventListener } from '../useEventListener'
import type { ConfigurableWindow } from '../_configurable'
import { defaultWindow } from '../_configurable'

export interface UseActiveElementOptions extends ConfigurableWindow {
ignoreBody?: MaybeRef<boolean>
}

/**
* Reactive `document.activeElement`
*
* @see https://vueuse.org/useActiveElement
* @param options
*/
export function useActiveElement<T extends HTMLElement>(options: ConfigurableWindow = {}) {
const { window = defaultWindow } = options
export function useActiveElement<T extends HTMLElement>(options: UseActiveElementOptions = {}) {
const { window = defaultWindow, ignoreBody } = options
const activeElement = computedWithControl(
() => null,
() => window?.document.activeElement as T | null | undefined,
Expand All @@ -21,9 +26,17 @@ export function useActiveElement<T extends HTMLElement>(options: ConfigurableWin
if (event.relatedTarget === null)
return

if (ignoreBody && window?.document.activeElement?.tagName === 'BODY')
return

activeElement.trigger()
}, true)
useEventListener(window, 'focus', () => {
if (ignoreBody && window?.document.activeElement?.tagName === 'BODY')
return

activeElement.trigger()
}, true)
useEventListener(window, 'focus', activeElement.trigger, true)
}

return activeElement
Expand Down
10 changes: 4 additions & 6 deletions packages/core/useFocusWithin/demo.vue
Expand Up @@ -4,22 +4,20 @@ import { useFocusWithin } from '@vueuse/core'

const target = ref()

const { focused } = useFocusWithin(target)
const { focused } = useFocusWithin(target, { ignoreBody: true })
</script>

<template>
<div class="flex flex-col items-center">
<form
ref="target"
class="shadow bg-base border-main rounded max-w-96 mx-auto p-8"
>
<form ref="target" class="shadow bg-base border-main rounded max-w-96 mx-auto p-8">
<input type="text" placeholder="First Name">
<input type="text" placeholder="Last Name">
<input type="text" placeholder="Email">
<input type="text" placeholder="Password">
</form>
<div mt2>
Focus in form: <BooleanDisplay :value="focused" />
Focus in form:
<BooleanDisplay :value="focused" />
</div>
</div>
</template>
5 changes: 3 additions & 2 deletions packages/core/useFocusWithin/index.ts
Expand Up @@ -2,8 +2,8 @@ import type { ComputedRef } from 'vue-demi'
import { computed } from 'vue-demi'
import type { MaybeElementRef } from '../unrefElement'
import { unrefElement } from '../unrefElement'
import type { UseActiveElementOptions } from '../useActiveElement'
import { useActiveElement } from '../useActiveElement'
import type { ConfigurableWindow } from '../_configurable'
export interface UseFocusWithinReturn {
/**
* True if the element or any of its descendants are focused
Expand All @@ -18,7 +18,8 @@ export interface UseFocusWithinReturn {
* @param target The target element to track
* @param options Focus within options
*/
export function useFocusWithin(target: MaybeElementRef, options: ConfigurableWindow = {}): UseFocusWithinReturn {

export function useFocusWithin(target: MaybeElementRef, options: UseActiveElementOptions = {}): UseFocusWithinReturn {
const activeElement = useActiveElement(options)
const targetElement = computed(() => unrefElement(target))
const focused = computed(() => targetElement.value && activeElement.value ? targetElement.value.contains(activeElement.value) : false)
Expand Down