0
# Element Tracking
1
2
Components and directives for tracking element properties like size, position, visibility, and bounding rectangles.
3
4
## Capabilities
5
6
### UseElementBounding Component
7
8
Tracks element bounding rectangle information with reactive updates.
9
10
```typescript { .api }
11
/**
12
* Component that tracks element bounding rectangle
13
* @example
14
* <UseElementBounding v-slot="{ x, y, width, height }">
15
* <div>Position: {{ x }}, {{ y }} Size: {{ width }}x{{ height }}</div>
16
* </UseElementBounding>
17
*/
18
interface UseElementBoundingProps extends RenderableComponent {
19
/** ResizeObserver box type @default 'content-box' */
20
box?: ResizeObserverBoxOptions;
21
}
22
23
/** Slot data exposed by UseElementBounding component */
24
interface UseElementBoundingReturn {
25
/** Distance from left edge of viewport */
26
x: Ref<number>;
27
/** Distance from top edge of viewport */
28
y: Ref<number>;
29
/** Distance from top edge of viewport (alias for y) */
30
top: Ref<number>;
31
/** Distance from right edge of viewport */
32
right: Ref<number>;
33
/** Distance from bottom edge of viewport */
34
bottom: Ref<number>;
35
/** Distance from left edge of viewport (alias for x) */
36
left: Ref<number>;
37
/** Element width */
38
width: Ref<number>;
39
/** Element height */
40
height: Ref<number>;
41
/** Update the bounding information manually */
42
update: () => void;
43
}
44
45
type ResizeObserverBoxOptions = 'border-box' | 'content-box' | 'device-pixel-content-box';
46
```
47
48
**Usage Examples:**
49
50
```vue
51
<template>
52
<!-- Basic usage -->
53
<UseElementBounding v-slot="{ x, y, width, height }">
54
<div class="box">
55
Position: ({{ Math.round(x) }}, {{ Math.round(y) }})
56
<br>
57
Size: {{ Math.round(width) }} × {{ Math.round(height) }}
58
</div>
59
</UseElementBounding>
60
61
<!-- Access all properties -->
62
<UseElementBounding v-slot="{ top, right, bottom, left, width, height, update }">
63
<div class="element">
64
<p>Bounding Rectangle:</p>
65
<ul>
66
<li>Top: {{ Math.round(top) }}px</li>
67
<li>Right: {{ Math.round(right) }}px</li>
68
<li>Bottom: {{ Math.round(bottom) }}px</li>
69
<li>Left: {{ Math.round(left) }}px</li>
70
<li>Width: {{ Math.round(width) }}px</li>
71
<li>Height: {{ Math.round(height) }}px</li>
72
</ul>
73
<button @click="update">Manual Update</button>
74
</div>
75
</UseElementBounding>
76
77
<!-- Custom wrapper element -->
78
<UseElementBounding as="section" v-slot="{ width, height }">
79
<div>Section size: {{ width }} × {{ height }}</div>
80
</UseElementBounding>
81
</template>
82
83
<script setup>
84
import { UseElementBounding } from '@vueuse/components';
85
</script>
86
```
87
88
### vElementBounding Directive
89
90
Directive for element bounding tracking without component wrapper.
91
92
```typescript { .api }
93
/**
94
* Directive for tracking element bounding rectangle
95
* @example
96
* <div v-element-bounding="handleBounding">Track bounding</div>
97
* <div v-element-bounding="[handleBounding, options]">With options</div>
98
*/
99
type ElementBoundingHandler = (rect: UseElementBoundingReturn) => void;
100
101
interface VElementBoundingValue {
102
/** Simple handler function */
103
handler: ElementBoundingHandler;
104
/** Handler with options tuple */
105
handlerWithOptions: [ElementBoundingHandler, UseResizeObserverOptions];
106
}
107
108
interface UseResizeObserverOptions {
109
/** ResizeObserver box type @default 'content-box' */
110
box?: ResizeObserverBoxOptions;
111
}
112
```
113
114
**Usage Examples:**
115
116
```vue
117
<template>
118
<!-- Simple tracking -->
119
<div v-element-bounding="handleBounding" class="tracked-element">
120
Tracked element
121
</div>
122
123
<!-- With options -->
124
<div v-element-bounding="[handleBounding, { box: 'border-box' }]">
125
Border-box tracking
126
</div>
127
</template>
128
129
<script setup>
130
import { vElementBounding } from '@vueuse/components';
131
132
function handleBounding(rect) {
133
console.log('Element bounds:', {
134
x: rect.x.value,
135
y: rect.y.value,
136
width: rect.width.value,
137
height: rect.height.value
138
});
139
}
140
</script>
141
```
142
143
### UseElementSize Component
144
145
Tracks element dimensions with reactive updates.
146
147
```typescript { .api }
148
/**
149
* Component that tracks element size
150
* @example
151
* <UseElementSize v-slot="{ width, height }">
152
* <div>Size: {{ width }}x{{ height }}</div>
153
* </UseElementSize>
154
*/
155
interface UseElementSizeProps extends RenderableComponent {
156
/** Initial size values */
157
initialSize?: { width: number; height: number };
158
/** ResizeObserver box type @default 'content-box' */
159
box?: ResizeObserverBoxOptions;
160
}
161
162
/** Slot data exposed by UseElementSize component */
163
interface UseElementSizeReturn {
164
/** Element width */
165
width: Ref<number>;
166
/** Element height */
167
height: Ref<number>;
168
/** Stop watching for size changes */
169
stop: () => void;
170
}
171
```
172
173
**Usage Examples:**
174
175
```vue
176
<template>
177
<!-- Basic usage -->
178
<UseElementSize v-slot="{ width, height }">
179
<div class="resizable">
180
Current size: {{ Math.round(width) }} × {{ Math.round(height) }}
181
</div>
182
</UseElementSize>
183
184
<!-- With initial size -->
185
<UseElementSize
186
v-slot="{ width, height, stop }"
187
:initial-size="{ width: 300, height: 200 }"
188
>
189
<textarea
190
:style="{ width: width + 'px', height: height + 'px' }"
191
@focus="() => {}"
192
@blur="stop"
193
>
194
Resizable textarea
195
</textarea>
196
</UseElementSize>
197
198
<!-- Custom box type -->
199
<UseElementSize box="border-box" v-slot="{ width, height }">
200
<div class="border-tracked">
201
Border-box size: {{ width }} × {{ height }}
202
</div>
203
</UseElementSize>
204
</template>
205
206
<script setup>
207
import { UseElementSize } from '@vueuse/components';
208
</script>
209
```
210
211
### vElementSize Directive
212
213
Directive for element size tracking without component wrapper.
214
215
```typescript { .api }
216
/**
217
* Directive for tracking element size
218
* @example
219
* <div v-element-size="handleSize">Track size</div>
220
* <div v-element-size="[handleSize, options]">With options</div>
221
*/
222
type ElementSizeHandler = (size: UseElementSizeReturn) => void;
223
224
interface VElementSizeValue {
225
/** Simple handler function */
226
handler: ElementSizeHandler;
227
/** Handler with options tuple */
228
handlerWithOptions: [ElementSizeHandler, UseElementSizeOptions];
229
}
230
231
interface UseElementSizeOptions {
232
/** Initial size values */
233
initialSize?: { width: number; height: number };
234
/** ResizeObserver box type @default 'content-box' */
235
box?: ResizeObserverBoxOptions;
236
}
237
```
238
239
**Usage Examples:**
240
241
```vue
242
<template>
243
<!-- Simple tracking -->
244
<div v-element-size="handleSize" class="size-tracked">
245
Size tracked element
246
</div>
247
248
<!-- With options -->
249
<div v-element-size="[handleSize, { box: 'border-box', initialSize: { width: 100, height: 100 } }]">
250
Custom size tracking
251
</div>
252
</template>
253
254
<script setup>
255
import { vElementSize } from '@vueuse/components';
256
257
function handleSize(size) {
258
console.log('Element size changed:', {
259
width: size.width.value,
260
height: size.height.value
261
});
262
}
263
</script>
264
```
265
266
### UseElementVisibility Component
267
268
Tracks element visibility within the viewport using Intersection Observer.
269
270
```typescript { .api }
271
/**
272
* Component that tracks element visibility in viewport
273
* @example
274
* <UseElementVisibility v-slot="{ isVisible }">
275
* <div>Visible: {{ isVisible }}</div>
276
* </UseElementVisibility>
277
*/
278
interface UseElementVisibilityProps extends RenderableComponent {
279
/** Intersection observer options */
280
options?: IntersectionObserverInit;
281
/** Scroll target element @default window */
282
scrollTarget?: MaybeRefOrGetter<HTMLElement | null | undefined>;
283
}
284
285
/** Slot data exposed by UseElementVisibility component */
286
interface UseElementVisibilityReturn {
287
/** Whether the element is visible in viewport */
288
isVisible: Ref<boolean>;
289
/** Stop the visibility observer */
290
stop: () => void;
291
}
292
293
interface IntersectionObserverInit {
294
/** Root element for intersection @default null */
295
root?: Element | null;
296
/** Root margin @default '0px' */
297
rootMargin?: string;
298
/** Visibility threshold @default 0 */
299
threshold?: number | number[];
300
}
301
```
302
303
**Usage Examples:**
304
305
```vue
306
<template>
307
<!-- Basic visibility tracking -->
308
<UseElementVisibility v-slot="{ isVisible }">
309
<div class="tracked-element" :class="{ visible: isVisible }">
310
{{ isVisible ? 'I am visible!' : 'I am hidden' }}
311
</div>
312
</UseElementVisibility>
313
314
<!-- With custom threshold -->
315
<UseElementVisibility
316
v-slot="{ isVisible, stop }"
317
:options="{ threshold: 0.5 }"
318
>
319
<div class="half-visible">
320
{{ isVisible ? '50% visible' : 'Less than 50% visible' }}
321
<button @click="stop">Stop tracking</button>
322
</div>
323
</UseElementVisibility>
324
325
<!-- With root margin -->
326
<UseElementVisibility
327
v-slot="{ isVisible }"
328
:options="{ rootMargin: '100px' }"
329
>
330
<div class="early-trigger">
331
Triggers 100px before entering viewport: {{ isVisible }}
332
</div>
333
</UseElementVisibility>
334
</template>
335
336
<script setup>
337
import { UseElementVisibility } from '@vueuse/components';
338
</script>
339
340
<style>
341
.tracked-element {
342
padding: 20px;
343
background: #f0f0f0;
344
transition: background 0.3s;
345
}
346
347
.tracked-element.visible {
348
background: #90EE90;
349
}
350
</style>
351
```
352
353
### vElementVisibility Directive
354
355
Directive for element visibility tracking without component wrapper.
356
357
```typescript { .api }
358
/**
359
* Directive for tracking element visibility in viewport
360
* @example
361
* <div v-element-visibility="handleVisibility">Track visibility</div>
362
* <div v-element-visibility="[handleVisibility, options]">With options</div>
363
*/
364
type ElementVisibilityHandler = (isVisible: boolean, entry: IntersectionObserverEntry) => void;
365
366
interface VElementVisibilityValue {
367
/** Simple handler function */
368
handler: ElementVisibilityHandler;
369
/** Handler with options tuple */
370
handlerWithOptions: [ElementVisibilityHandler, UseElementVisibilityOptions];
371
}
372
373
interface UseElementVisibilityOptions {
374
/** Intersection observer options */
375
options?: IntersectionObserverInit;
376
/** Scroll target element @default window */
377
scrollTarget?: MaybeRefOrGetter<HTMLElement | null | undefined>;
378
}
379
```
380
381
**Usage Examples:**
382
383
```vue
384
<template>
385
<!-- Simple visibility tracking -->
386
<div v-element-visibility="handleVisibility" class="visibility-tracked">
387
Visibility tracked element
388
</div>
389
390
<!-- With threshold -->
391
<div v-element-visibility="[handleVisibility, { options: { threshold: 0.8 } }]">
392
80% visibility required
393
</div>
394
395
<!-- Multiple thresholds -->
396
<div v-element-visibility="[handleMultipleVisibility, {
397
options: { threshold: [0, 0.25, 0.5, 0.75, 1.0] }
398
}]">
399
Progressive visibility tracking
400
</div>
401
</template>
402
403
<script setup>
404
import { vElementVisibility } from '@vueuse/components';
405
406
function handleVisibility(isVisible, entry) {
407
console.log('Element visibility changed:', isVisible);
408
console.log('Intersection ratio:', entry.intersectionRatio);
409
}
410
411
function handleMultipleVisibility(isVisible, entry) {
412
const ratio = Math.round(entry.intersectionRatio * 100);
413
console.log(`Element is ${ratio}% visible`);
414
}
415
</script>
416
```
417
418
### vElementHover Directive
419
420
Directive for hover state detection on elements.
421
422
```typescript { .api }
423
/**
424
* Directive for detecting hover state on elements
425
* @example
426
* <div v-element-hover="handleHover">Hover me</div>
427
* <div v-element-hover="[handleHover, options]">With options</div>
428
*/
429
type ElementHoverHandler = (isHovering: boolean) => void;
430
431
interface VElementHoverValue {
432
/** Simple handler function */
433
handler: ElementHoverHandler;
434
/** Handler with options tuple */
435
handlerWithOptions: [ElementHoverHandler, UseElementHoverOptions];
436
}
437
438
interface UseElementHoverOptions {
439
/** Delay in ms before triggering hover @default 0 */
440
delayEnter?: number;
441
/** Delay in ms before removing hover @default 0 */
442
delayLeave?: number;
443
}
444
```
445
446
**Usage Examples:**
447
448
```vue
449
<template>
450
<!-- Simple hover detection -->
451
<div v-element-hover="handleHover" class="hover-element">
452
{{ isHovering ? 'Hovering!' : 'Not hovering' }}
453
</div>
454
455
<!-- With delays -->
456
<div v-element-hover="[handleDelayedHover, { delayEnter: 200, delayLeave: 500 }]">
457
Delayed hover (200ms enter, 500ms leave)
458
</div>
459
</template>
460
461
<script setup>
462
import { ref } from 'vue';
463
import { vElementHover } from '@vueuse/components';
464
465
const isHovering = ref(false);
466
467
function handleHover(hovering) {
468
isHovering.value = hovering;
469
console.log('Hover state:', hovering);
470
}
471
472
function handleDelayedHover(hovering) {
473
console.log('Delayed hover state:', hovering);
474
}
475
</script>
476
```
477
478
## Type Definitions
479
480
```typescript { .api }
481
/** Common types used across element tracking */
482
type MaybeRefOrGetter<T> = T | Ref<T> | (() => T);
483
type MaybeElementRef = HTMLElement | null | undefined;
484
485
interface RenderableComponent {
486
/** The element that the component should be rendered as @default 'div' */
487
as?: object | string;
488
}
489
490
/** Position information */
491
interface Position {
492
x: number;
493
y: number;
494
}
495
496
/** Size information */
497
interface Size {
498
width: number;
499
height: number;
500
}
501
```