@opuu/inview
Version:
Lightweight JavaScript library for viewport detection with debounced callbacks - intersection observer, lazy loading, scroll animations, infinite scroll, element visibility tracking with TypeScript support. Zero dependencies, ~1KB gzipped.
342 lines (248 loc) • 7.59 kB
Markdown
A lightweight JavaScript library for detecting when elements enter or exit the viewport.
[](https://badge.fury.io/js/%40opuu%2Finview)
[](https://opensource.org/licenses/MIT)
```bash
npm install @opuu/inview
```
```javascript
import InView from "@opuu/inview";
const observer = new InView(".my-element");
observer.on("enter", (event) => {
console.log("Element visible:", event.percentage + "%");
});
observer.on("exit", (event) => {
console.log("Element hidden");
});
```
```javascript
import InView from "@opuu/inview";
const observer = new InView(".lazy-image");
observer.on("enter", (event) => {
const img = event.target;
img.src = img.dataset.src;
img.classList.add("loaded");
});
```
```javascript
import InView from "@opuu/inview";
const observer = new InView(".animate-on-scroll");
observer.on("enter", (event) => {
event.target.classList.add("animate");
});
observer.on("exit", (event) => {
event.target.classList.remove("animate");
});
```
```javascript
import InView from "@opuu/inview";
const observer = new InView(".load-more-trigger");
observer.on("enter", (event) => {
loadMoreContent();
});
```
You can pass options when creating an InView instance:
```javascript
const observer = new InView({
selector: ".my-element",
delay: 100, // Debounce delay in ms
precision: "high", // "low", "medium", or "high"
single: true, // Only observe first element
});
```
| Option | Default | Description |
| ----------- | ---------- | --------------------------------------- |
| `selector` | required | CSS selector for elements to observe |
| `delay` | `0` | Debounce delay in milliseconds |
| `precision` | `"medium"` | Observation precision level |
| `single` | `false` | Only observe the first matching element |
```javascript
observer.on("enter", callback); // Element enters viewport
observer.on("exit", callback); // Element exits viewport
observer.pause(); // Pause observation
observer.resume(); // Resume observation
observer.setDelay(100); // Update debounce delay
observer.destroy(); // Clean up
```
Callbacks receive an event object with:
```javascript
{
percentage: 75, // Visibility percentage (0-100)
target: Element, // The observed element
time: 1234567890, // Timestamp
event: "enter" | "exit" // Event type
// ... other properties
}
```
```html
<script type="module">
import InView from "https://cdn.jsdelivr.net/npm/@opuu/inview/dist/inview.js";
</script>
```
MIT
```javascript
import InView from "@opuu/inview";
const imageObserver = new InView(".lazy-image");
imageObserver.on("enter", (event) => {
const img = event.target;
if (img.dataset.src) {
img.src = img.dataset.src;
img.removeAttribute("data-src");
img.classList.add("loaded");
}
});
```
```javascript
import InView from "@opuu/inview";
const animationObserver = new InView({
selector: ".animate-on-scroll",
precision: "medium",
});
animationObserver.on("enter", (event) => {
if (event.percentage > 50) {
// Trigger when 50% visible
event.target.classList.add("animate-in");
}
});
animationObserver.on("exit", (event) => {
event.target.classList.remove("animate-in");
});
```
```javascript
import InView from "@opuu/inview";
const infiniteObserver = new InView({
selector: ".load-more-trigger",
single: true,
delay: 200, // Debounce to prevent multiple rapid loads
});
infiniteObserver.on("enter", async (event) => {
try {
const newContent = await loadMoreContent();
document.getElementById("content").appendChild(newContent);
} catch (error) {
console.error("Failed to load content:", error);
}
});
```
```javascript
import InView from "@opuu/inview";
const performanceObserver = new InView({
selector: ".track-visibility",
delay: 300, // Debounce analytics calls
});
performanceObserver.on("enter", (event) => {
// Track when important content becomes visible
analytics.track("element_viewed", {
element_id: event.target.id,
visibility_percentage: event.percentage,
timestamp: event.time,
});
});
```
```jsx
import { useEffect, useRef } from "react";
import InView from "@opuu/inview";
function LazyImage({ src, alt }) {
const imgRef = useRef(null);
useEffect(() => {
const observer = new InView({
selector: imgRef.current,
single: true,
});
observer.on("enter", (event) => {
event.target.src = src;
event.target.classList.add("loaded");
});
return () => observer.destroy();
}, [src]);
return <img ref={imgRef} alt={alt} className="lazy-image" />;
}
```
For Vue.js applications, use the dedicated [@opuu/inview-vue](https://www.npmjs.com/package/@opuu/inview-vue) package which provides `v-inview` and `v-outview` directives.
```bash
npm install @opuu/inview-vue
```
```typescript
import { Component, ElementRef, OnInit, OnDestroy } from "@angular/core";
import InView from "@opuu/inview";
@Component({
selector: "app-lazy-content",
template: '<div class="lazy-content">Content</div>',
})
export class LazyContentComponent implements OnInit, OnDestroy {
private observer: InView;
constructor(private elementRef: ElementRef) {}
ngOnInit() {
this.observer = new InView({
selector: this.elementRef.nativeElement,
single: true,
});
this.observer.on("enter", (event) => {
event.target.classList.add("visible");
});
}
ngOnDestroy() {
this.observer?.destroy();
}
}
```
1. **Use appropriate precision levels**: Start with "medium" and only use "high" when necessary
2. **Clean up observers**: Always call `destroy()` when components unmount
3. **Debounce rapid changes**: Use the `delay` option for expensive operations
4. **Single element optimization**: Use `single: true` when observing only one element
```javascript
// Good: Clean up when done
const observer = new InView(".my-element");
// ... use observer
observer.destroy(); // Important!
// Good: Store reference for cleanup
class MyComponent {
constructor() {
this.observer = new InView(".element");
}
destroy() {
this.observer.destroy();
}
}
```
InView is built on the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) and supports all modern browsers:
- Chrome 51+
- Firefox 55+
- Safari 12.1+
- Edge 15+
For older browser support, consider using an [Intersection Observer polyfill](https://github.com/w3c/IntersectionObserver/tree/main/polyfill).
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
[](LICENSE) - feel free to use this project in your commercial and personal projects.
**Obaydur Rahman**
- GitHub: [@opuu](https://github.com/opuu)
- Website: [opu.rocks](https://opu.rocks)
- [@opuu/inview-vue](https://www.npmjs.com/package/@opuu/inview-vue) - Vue.js directives for InView