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 🙏

© 2026 – Pkg Stats / Ryan Hefner

dynamic-text-anchors

v2.1.3

Published

Dynamic Text Anchors

Readme

DynamicTextAnchors (DTA)

NPM Version

Table of contents


Annotation

The thesis deals with the design of algorithms that would enable the storage of so-called text anchors (labels, notes etc.) in static and dynamic text (XML format) so that they can be re- inserted into the text even after its editing (and possibly evaluate the error during insertion). Such a program should then be usable as a library for e.g., web applications.

A note on project history and documentation > This library was originally developed as a graduation thesis on the design of algorithms for text anchors in static and dynamic text. The codebase has since been fully rewritten to improve its architecture, performance, and maintainability. The DOCUMENTATION file reflects the state of the project at the time of the thesis's creation and is not intended to be updated with the current project (original code can be found in thesis-legacy folder). This README reflects the project's current state.

Technologies used

  • lib: TS, CSS
  • demo: Vue.JS, TS, SCSS

Installation

npm i dynamic-text-anchors

Styles

For default styling of the custom elements, include the following CSS file in your project. This is optional; you can also create your own styles or easily override them.

import 'dynamic-text-anchors/dist/lib/utils/_styles.css';

How to use

The library's core is the DTA class, which serves as the main entry point and orchestrator. You instantiate it with a root DOM element and then interact with its public methods to manage anchors and renderers.

Basic Usage Example

Here is a quick example of how to set up the library and create highlights from a user's selection.

// Assume your project has an HTML file with an element with id="content"

import { DTA, InlineRenderer } from 'dynamic-text-anchors';
import 'dynamic-text-anchors/dist/lib/utils/_styles.css'; // Optional, for default styling

// 1. Get the root element you want to work with.
const contentElement = document.getElementById('content');

if (contentElement) {
    // 2. Instantiate the DTA library
    const dta = new DTA();

    // 3. Create a renderer for how anchors will be displayed, linking it to the content element.
    const inlineRenderer = new InlineRenderer(contentElement);

    // 4. Add the renderer to the DTA instance.
    dta.addRenderer(inlineRenderer);

    // 5. Add a listener to the root element to create an anchor on selection.
    contentElement.addEventListener('mouseup', () => {
        dta.createAnchorFromSelection();
    });
}

DTA methods

The DTA class manages the lifecycle of all anchors and renderers.

  • constructor()
    Instantiates the DTA library instance.

  • addRenderer(renderer: RendererI): void
    Adds a new renderer to the DTA instance. This is how you tell DTA how to display anchors.

  • removeRenderer(renderer: RendererI): void
    Removes a renderer and its rendered anchors from the DTA instance.

  • createAnchorFromSelection(selection?: Selection | null): void
    Creates an anchor based on the current user selection. If no selection is provided, it uses the active one.

  • createAnchorFromRange(range: Range): void
    Creates an anchor from a given Range object.

  • removeAnchor(anchor: AnchorI): void
    Destroys a specific anchor and removes it from the DOM.

  • canAnchorMerge(anchor: AnchorI, direction: MergeDirection): boolean
    Checks if an anchor can be merged with an adjacent anchor in a given direction ('left' or 'right').

  • mergeAnchor(anchor: AnchorI, direction: MergeDirection): void
    Merges an anchor with an adjacent anchor in the specified direction.

  • serialize(): SerializedDTA
    Returns a serializable JSON object of all active anchors. This can be used to save the anchors to a database.

  • deserialize(data: SerializedDTA): void
    Loads a set of anchors from a serialized JSON object, rendering them in the DOM.

  • clearAnchors(): void
    Destroys all anchors managed by the instance.

  • clearRenderers(): void
    Destroys all renderers and their rendered content.

  • destroy(): void
    Destroys the DTA instance, all anchors, and all renderers.


Anchor methods

The Anchor class represents a single text highlight. You typically interact with it via the DTA instance, but you can also manipulate it directly.

  • constructor(range: DTARange)
    Instantiates a new Anchor object from a DTARange object. A unique id is automatically generated.

  • setColor(bg: ColorValue, fg?: ColorValue): void
    Sets the background and optional foreground colors of the anchor. The fg defaults to an inverted version of bg.

  • setRange(range: DTARange): void
    Updates the anchor's underlying range.

  • acceptChange(): void
    Marks the anchor’s change as accepted (e.g., removes the "changed" state).

  • requestFocus(focus: boolean): void
    Emits a request to the Event Bus for the anchor's associated element to be focused or unfocused.

  • requestMerge(direction: MergeDirection): void
    Emits a request to the Event Bus for the anchor to be merged with a neighboring anchor.

  • serialize(): SerializedAnchor
    Returns a serializable JSON object of the anchor's data.

  • static deserialize(data: SerializedAnchor): Anchor
    Creates a new Anchor instance from a serialized JSON object.

  • destroy(): void
    Removes the anchor from the DTA instance and the DOM.


AnchorElement methods

The AnchorElement is a custom DOM element that represents the rendered anchor.

  • render(): void
    Renders the element into the DOM.

  • requestFocus(focus: boolean): void
    Requests that the element gain or lose focus.

  • requestHover(hover: boolean): void
    Requests that the element gain or lose hover state.

  • requestMerge(direction: MergeDirection): void
    Requests a merge action for this anchor element.

  • requestDestroy(): void
    Requests removal of this anchor element.

  • toggleFocus(focus: boolean): void
    Applies or removes the focus state on the element.

  • toggleHover(hover: boolean): void
    Applies or removes the hover state on the element.

  • destroy(): void
    Removes the element from the DOM and performs cleanup.


Renderers

Renderers are responsible for the visual representation of anchors. The library provides abstract base classes to help you create your own, and two built-in renderers to get you started.

Default Renderers

  • InlineRenderer: Renders anchors as inline elements that wrap the text.
  • ListRenderer: Renders anchors as list items in a separate container.

How to Use

You must add a renderer to a DTA instance to see any anchors rendered.

const dta = new DTA();
const listRenderer = new ListRenderer(document.getElementById('list-container'));
dta.addRenderer(listRenderer);

Methods

  • renderAnchor(anchor: AnchorI): void
    Renders a specific anchor in the DOM.

  • updateAnchor(anchor: AnchorI): void
    Updates the visual representation of a rendered anchor. The default implementation calls removeAnchor and then renderAnchor.

  • removeAnchor(anchor: AnchorI): void
    Removes a rendered anchor from the DOM.

  • focusAnchor(anchor: AnchorI, focus: boolean): void
    Toggles the focus state of the rendered anchor element.

  • hoverAnchor(anchor: AnchorI, hover: boolean): void
    Toggles the hover state of the rendered anchor element.

  • destroy(): void
    Destroys the renderer and removes all of its rendered content from the DOM.


Event Bus

The library uses a global EventBus to handle communication between different components. All components have an event bus instance you can listen to.

How to Use

import { EventBus } from "dynamic-text-anchors";

const eventBus = EventBus.getInstance();

eventBus.on("anchor:create", (event) => {
    console.log("New anchor created:", event.payload.anchor.id);
}, this);

Methods

  • on<K extends keyof EventMap>(type: K, fn: (event: Event<K>) => void, target: any): void
    Subscribes a function to a specific event type. The target is used for cleanup.

  • off<K extends keyof EventMap>(type: K, fn: (event: Event<K>) => void, target: any): void
    Unsubscribes a specific function from an event.

  • offAll(target: any): void
    Removes all subscriptions for a given target object.

  • emit<K extends keyof EventMap>(event: Event<K>): void
    Emits an event, triggering all subscribed functions.


Utility Methods

A small set of utility functions are also exported for convenience.

DOM Utilities

  • getSelection(): Selection | null
    Returns the active Selection object or null.

  • buildTextIndex(root: Node): TextIndex
    Creates an index of all text nodes within a root element, mapping character positions to DOM nodes.

  • deserializeRange(range: DTARange, root: Node): Range | null
    Reconstructs a Range object from a serialized DTARange object within a given root node.

  • getAllTextNodes(range: Range): Text[]
    Returns all text nodes within a given Range object.

Color Utilities

  • isValidHexColor(color: string): boolean
    Checks if a string is a valid hex color string.

  • invertHexColor(hex: string): ColorValue
    Inverts a hex color to be readable on the original color.

  • adjustColorBrightness(hex: string, percent: number): ColorValue
    Adjusts the brightness of a hex color by a given percentage.

  • generateRandomColor(): ColorValue
    Generates a random valid hex color.

String Utilities

  • normalizeString(str: string): string
    Returns a string in a normalized form by removing diacritics, punctuation, numbers, and collapsing whitespace.

  • escapeRegExp(str: string): string
    Escapes a string for use in a regular expression.

  • calculateStringSimilarity(str1: string, str2: string): number
    Calculates the similarity between two strings using the Levenshtein distance algorithm.