npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

target-observer

v1.1.2

Published

With Target Observer, you can easily trigger animations, lazy load content, or handle visibility-based logic in your application.

Downloads

394

Readme

Target Observer

Table of Contents

Target Observer is designed to help developers efficiently monitor and manage the visibility of elements within the viewport.

It provides a set of tools including the useInView hook, InViewProvider, Target, and ObserveZone components. These tools enable seamless tracking of when specific elements, identified as "targets" enter or exit the viewport.

With Target Observer, you can easily trigger animations, lazy load content, or handle visibility-based logic in your application. The library is optimized for performance and ease of use, making it a robust solution for building responsive and dynamic user interfaces.

Checkout the demo here.

Component API

InViewProvider

The InViewProvider component is a wrapper for tracking the visibility of multiple Target components within the viewport. The component monitors when each target enters or exits the viewport, enabling you to manage and respond to the in-view state of these elements within your application.

| Property | Default | Description | | -------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | targetIds | - | An array of strings that specifies the IDs of the Target components to be tracked by the InViewProvider. | | firstTargetActiveOnMount | true | By default, the first target is activated when the component mounts. If set to false, it will only update once the user starts scrolling and the target enters the viewport. |

useInView

The useInView hook tracks when a target element enters the viewport, making it ideal for dynamically updating the styling of items or chapters in a table of contents on a documentation site.

ObserveZone

The ObserveZone is an invisible component that defines a specific area within which visibility of child elements is tracked.

| Property | Default | Description | | ----------- | ------- | ---------------------------------------------------------------------------------- | | height | 50vh | An optional property for adjusting the observing area's height. | | className | - | An optional property for defining class names. Use only for testing the component. |

Target

The Target component represents an element whose visibility is tracked within the ObserveZone. Each Target should have a unique ID that matches an entry in the targetIds array provided to the InViewProvider.

The component’s visibility state can be used to trigger actions, such as animations or lazy loading, based on whether the target is in view or out of view.

| Property | Default | Description | | ---------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | id | - | The id of the component to track and trigger state updates. | | as | div | The element or component the Target should render as. | | entryThreshold | 0.5 | The percentage of observing zone height that should be used to trigger the observer. The target element when inside this area will update the inView property to true. Accepts value between 0 and 1. Min is 0.1 and Max is 0.9. |

Examples:

React with CSS

import clsx from 'clsx'
import { useInView, InViewProvider, ObserveZone, Target } from 'target-observer'

const targetIds = [
    'section#1',
    'section#2',
    'section#3',
    'section#4',
    'section#5'
]
export default function Example() {
    return (
        <InViewProvider targetIds={targetIds}>
            <div className='container'>
                <Navigation />

                {/* must use position:relative in parent for ObserveZone to work */}
                <div className='target-wrapper'>
                    {/* add this invisible component to track the target */}
                    <ObserveZone
                        // optional height property, default is 50vh
                        height='70vh'
                        // optional className property, use only for testing
                        className='observer'
                    />

                    {targetIds.map((targetId) => (
                        <Target
                            key={targetId}
                            // must specify the id property for target to work
                            id={targetId}
                            // the html element you want to render
                            as='section'
                            // add styling to your target
                            className='target'
                            // the height in percent of observing zone to trigger inView state
                            // default is 0.5 (50 percent)
                            entryThreshold={0.3}
                        >
                            Target: {targetId}
                        </Target>
                    ))}
                </div>
            </div>
        </InViewProvider>
    )
}

function Navigation() {
    const inView = useInView() // returns a record with boolean values

    return (
        <nav>
            <ul>
                {targetIds.map((targetId) => {
                    return (
                        <li
                            key={targetId}
                            className={clsx({
                                'list-active': inView[targetId]
                            })}
                        >
                            <a href={'#' + targetId}>{targetId}</a>
                        </li>
                    )
                })}
            </ul>
        </nav>
    )
}
nav {
    position: sticky;
    top: 2.5rem;
    height: 100vh;
}

ul {
    list-style-type: none;
}

li {
    width: 8rem;
    text-align: center;
    color: black;
}

a {
    text-decoration: none;
    font-size: 18px;
    color: inherit;
}

li:not(:first-child):not(:last-child),
section:not(:first-child):not(:last-child) {
    margin-top: 16px;
    margin-bottom: 16px;
}

.list-active {
    font-weight: 700;
    padding: 0.25rem 0 0.25rem 0;
    background-color: #ef4444;
    color: white;
}

.container {
    max-width: 80rem;
    width: 100%;
    margin-left: auto;
    margin-right: auto;
    padding: 1.25rem;
    display: flex;
    gap: 2.5rem;
}

.target-wrapper {
    position: relative;
    width: 100%;
}

.target {
    height: 90vh;
    border: 4px solid #e5e7eb;
    padding: 1.25rem;
}

.observer {
    outline: 4px solid rgba(239, 68, 68, 0.2);
    outline-offset: 8px;
}

React with Tailwind CSS

import clsx from 'clsx'
import { useInView, InViewProvider, ObserveZone, Target } from 'target-observer'

const targetIds = [
    'section#1',
    'section#2',
    'section#3',
    'section#4',
    'section#5'
]
export default function Example() {
    return (
        <InViewProvider targetIds={targetIds}>
            <div className='h-screen w-full'>
                <div className='max-w-7xl mx-auto w-full p-5 flex gap-10'>
                    <Navigation />
                    {/* must use position:relative in parent for ObserveZone to work */}
                    <div className='relative w-full space-y-5'>
                        {/* add this invisible component to track the target */}
                        <ObserveZone
                            // optional height property, default is 50vh
                            height='70vh'
                            // optional className property, use only for testing
                            className='ring-4 ring-offset-8 ring-red-500/20'
                        />

                        {targetIds.map((targetId) => (
                            <Target
                                key={targetId}
                                // must specify the id property for target to work
                                id={targetId}
                                // the html element you want to render
                                as='section'
                                // add styling to your target
                                className='h-[90vh] border-4 p-5 w-full'
                                // the height in percent of observing zone to trigger inView state
                                // default is 0.5 (50 percent)
                                entryThreshold={0.3}
                            >
                                Target: {targetId}
                            </Target>
                        ))}
                    </div>
                </div>
            </div>
        </InViewProvider>
    )
}

function Navigation() {
    const inView = useInView() // returns a record with boolean values
    return (
        <div className='sticky top-10 h-screen'>
            <ul className='space-y-4'>
                {targetIds.map((targetId) => (
                    <li
                        key={targetId}
                        className={clsx('text-center w-32', {
                            'font-bold py-1 bg-red-500 text-white':
                                inView[targetId]
                        })}
                    >
                        <a href={'#' + targetId}>{targetId}</a>
                    </li>
                ))}
            </ul>
        </div>
    )
}

Issues

Please file an issue for bugs, missing documentation, or unexpected behavior. Don't hesitate to make a PR for any issue.

File an issue