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

@nodebug/browser-element-finder

v1.0.8

Published

Browser Element Finder - Find elements by type and text content

Readme

@nodebug/browser-element-finder

A robust, agent-friendly JavaScript library for identifying DOM elements by type and/or text content, with full support for shadow DOM, iframes, and automation workflows.


Quickstart for Agents & Automation

Inject the library and find elements in any browser context (Selenium, Playwright, Puppeteer, or browser console):

// Find all visible buttons
const results = ElementFinder.findElement('button')

// Find a button by text (substring match)
const results = ElementFinder.findElement('button', 'Submit')

// Find by text only (any type)
const results = ElementFinder.findElement(null, 'seleniumbase')

// Find in all frames (default)
const results = ElementFinder.findElement('button')

// Highlight found elements
ElementFinder.highlight(results.elements.map((e) => e.element))

// Remove highlight
ElementFinder.unhighlight(results.elements.map((e) => e.element))

Agent/Automation Best Practices:

  • Always check frameIndex: -1 = main frame, 0+ = iframe (see below for iframe handling)
  • For iframe results, switch context before interacting (see Selenium/Playwright docs)
  • Use getValidTypes() to enumerate all supported semantic types
  • Use getSearchableAttributes() to see which attributes are searched for text

Features

  • Type-based element finding: Find elements by semantic type (button, textbox, link, dropdown, etc.)
  • Text content search: Search within element text, attributes, and placeholders
  • Shadow DOM support: Automatically traverses shadow roots to find nested elements
  • Iframe support: Automatically searches all frames (main document + iframes) by default
  • Visibility filtering: Optionally include or exclude hidden elements
  • Bounding box data: Returns position and dimensions for each found element
  • XPath-like type definitions: Extensible element type matching using XPath-like expressions
  • Optimized performance: O(n) innermost element filtering and efficient Set-based lookups

Installation

npm install @nodebug/browser-element-finder

Project Structure

browser-element-finder/
├── index.js                      # Browser-injected library (generated)
├── build.js                      # Build script to generate index.js
├── src/
│   ├── element-finder.js         # Canonical source (ES module)
│   ├── element-definitions.json  # XPath-like type definitions
│   └── searchable-attributes.json  # Attributes searched for text matching
├── tests/
│   ├── unit/                     # Unit tests
│   └── integration/              # Integration tests with HTML fixtures
└── coverage/                       # Test coverage reports

Usage Examples

In Browser Console or Automation Script

// Find all visible buttons
const results = ElementFinder.findElement('button')
// Find by text
const results = ElementFinder.findElement('button', 'Submit')
// Find by text only
const results = ElementFinder.findElement(null, 'seleniumbase')
// Include hidden elements
const results = ElementFinder.findElement('button', null, false, true)

Working with Iframes (Agent Pattern)

The library automatically searches all frames (main + iframes). For agent/automation use:

  • Main frame: item.frameIndex === -1 and item.element is available for direct interaction.
  • Iframe: item.frameIndex >= 0 and item.element is undefined. Use frameIndex to switch context, then re-run findElement inside the iframe to get interactable elements.

Example:

const results = ElementFinder.findElement('button')
for (const item of results.elements) {
  if (item.frameIndex === -1 && item.element) {
    // Interact directly
    item.element.click()
  } else if (item.frameIndex >= 0) {
    // Switch to iframe, then re-query
    // (agent/driver-specific code here)
  }
}

ESM Import

import {
  findElement,
  highlight,
  unhighlight,
  getValidTypes,
} from '@nodebug/browser-element-finder/src/element-finder.js'

// Find elements (requires DOM environment)
const results = findElement('button', 'Submit')

// Access metadata from results
results.elements.forEach((item) => {
  console.log('Tag:', item.tagName)
  console.log('Position:', item.boundingBox.x, item.boundingBox.y)
})

// Highlight elements (extract DOM elements from wrapper objects)
highlight(results.elements.map((e) => e.element))

CommonJS Import

const {
  findElement,
  highlight,
  unhighlight,
  getValidTypes,
} = require('@nodebug/browser-element-finder/src/element-finder.js')

Accessing Element Definitions and Searchable Attributes

The package exports JSON files containing element type definitions and searchable attributes:

// ESM - Import JSON directly
import ELEMENT_DEFINITIONS from '@nodebug/browser-element-finder/element-definitions.json' assert { type: 'json' }
import SEARCHABLE_ATTRIBUTES from '@nodebug/browser-element-finder/searchable-attributes.json' assert { type: 'json' }

// Get all valid element types
console.log(Object.keys(ELEMENT_DEFINITIONS)) // ['button', 'checkbox', 'dropdown', ...]

// Get searchable attributes
console.log(SEARCHABLE_ATTRIBUTES) // ['placeholder', 'value', 'data-test-id', ...]
// CommonJS - Use require
const ELEMENT_DEFINITIONS = require('@nodebug/browser-element-finder/element-definitions.json')
const SEARCHABLE_ATTRIBUTES = require('@nodebug/browser-element-finder/searchable-attributes.json')

API Summary

| Function | Description | | ------------------------------------------------------- | --------------------------------------------------------- | | findElement(type, text, exact, includeHidden, parent) | Find elements by type/text, returns { elements: [...] } | | highlight(elements, color, width) | Highlight elements with outline | | unhighlight(elements) | Remove highlight | | getValidTypes() | List all supported element types | | getBoundingBox(element) | Get bounding box for an element | | setSearchableAttributes(attributes) | Set custom attributes for text search | | getSearchableAttributes() | Get current searchable attributes | | matchesType(el, type) | Check if element matches a type | | matchesContent(el, value, exact) | Check if element matches text | | getAllElements(root) | Get all elements (with shadow DOM) | | getAllFrames(root, maxFrames) | Get all frames (main + iframes) | | parseXPath(expr, el, depth) | Parse XPath-like type expressions | | splitByOperator(expr, op) | Split XPath by operator |


findElement(type, text, exact, includeHidden, parent)

Finds elements matching the specified criteria. Searches all frames (main document + iframes) by default.

| Parameter | Type | Default | Description | | --------------- | --------- | ----------- | ----------------------------------------------------------------------------------------------------- | | type | string | "element" | Element type (see supported types below). Must be a string; throws TypeError for non-string values. | | text | string | null | Text to search for in content/attributes | | exact | boolean | false | Exact text match vs substring | | includeHidden | boolean | false | Include hidden elements | | parent | Element | null | Parent element to search within |

Returns: { elements: [{ element, boundingBox, tagName, frameIndex }] }

  • element: Raw DOM element (main frame only; for iframes, use frameIndex and re-query after switching context)
  • frameIndex: -1 for main frame, 0, 1, 2... for iframes

Agent/Automation Note: Iframe elements cannot be interacted with directly. Use frameIndex to switch context, then re-run findElement inside the iframe.

highlight(elements, color, width)

Highlights elements on the page with a colored outline.

| Parameter | Type | Default | Description | | ---------- | -------- | ------- | ----------------------- | | elements | Array | - | Elements to highlight | | color | string | 'red' | Outline color | | width | number | 3 | Outline width in pixels |

unhighlight(elements)

Removes highlighting from elements.

getValidTypes()

Returns an array of all valid element type names.

getBoundingBox(element)

Returns the bounding box for an element.

setSearchableAttributes(attributes)

Sets custom searchable attributes.

getSearchableAttributes()

Returns the current searchable attributes array.

matchesType(el, type)

Checks if an element matches the specified type definition.

matchesContent(el, value, exact)

Checks if an element matches the specified text content. Safely handles edge case elements that may throw errors on attribute access.

getAllElements(root)

Gets all elements including shadow DOM contents.

getAllFrames(root, maxFrames)

Gets all frames (main document + iframes) in the window. Returns array with frameIndex (-1 for main, 0+ for iframes). Cross-origin iframes (SecurityError) are automatically skipped with a specific warning message, while other errors are logged separately.

getConfig()

Returns the current configuration object.

parseXPath(expr, el, depth)

Parses XPath-like expressions for element type matching. The depth parameter is used internally for recursion tracking and has a maximum limit of 100 to prevent stack overflow from deeply nested expressions.

splitByOperator(expr, op)

Splits XPath expressions by operator (and/or).

Working with Iframes

The library automatically searches all frames (main document + iframes) by default. However, there are important limitations when working with iframe elements:

Iframe Element Limitations

const results = ElementFinder.findElement('button')

results.elements.forEach((item) => {
  if (item.frameIndex === -1) {
    // Main frame element - can interact directly
    console.log('Main frame element:', item.element)
    item.element.click() // Works
  } else {
    // Iframe element - element property is undefined
    console.log('Iframe element at frameIndex:', item.frameIndex)
    console.log('Bounding box:', item.boundingBox)
    // item.element is undefined - cannot interact directly
  }
})

Interacting with Iframe Elements

To interact with elements inside an iframe, you must switch the Selenium driver context:

// Find iframe elements
const results = await driver.executeScript(`
  return ElementFinder.findElement('button');
`)

// Switch to iframe and interact
const iframeElements = results.elements.filter((e) => e.frameIndex >= 0)
if (iframeElements.length > 0) {
  // Switch to the iframe (frameIndex 0 = first iframe)
  await driver.switchTo().frame(iframeElements[0].frameIndex)

  // Now find and interact with elements in the iframe
  const iframeResults = await driver.executeScript(`
    return ElementFinder.findElement('button');
  `)
  // These elements will have the element property since we're in the iframe context
}

Supported Element Types

| Type | Description | | ------------ | --------------------------------------------------- | | button | <button>, [role="button"], [type="button"] | | checkbox | <input type="checkbox">, [role="checkbox"] | | switch | Toggle switches, checkboxes with switch role | | slider | <input type="range">, [role="slider"] | | radio | <input type="radio">, [role="radio"] | | dropdown | <select>, [role="combobox"], [role="listbox"] | | textbox | <input>, <textarea>, [role="textbox"] | | link | <a>, [role="link"], [href] | | heading | <h1>-<h6>, [role="heading"] | | navigation | <nav>, [role="navigation"] | | list | <ul>, <ol>, [role="list"] | | listitem | <li>, [role="listitem"] | | menu | <menu>, [role="menu"] | | menuitem | [role="menuitem"] | | toolbar | [role="toolbar"] | | dialog | [role="dialog"] | | table | <table>, [role="table"] | | row | <tr>, [role="row"] | | column | <td>, <th>, [role="cell"] | | image | <img>, [role="img"] | | file | <input type="file"] | | element | Matches all elements |

Searchable Attributes

By default, the library searches these attributes (in priority order):

  • placeholder, value, data-test-id, data-testid, id
  • resource-id, name, aria-label, class, hint
  • title, tooltip, alt, src, aria-labelledby

Performance

The library is optimized for large DOM trees with efficient algorithms:

  • Innermost element filtering: O(n) algorithm using Set-based lookups instead of O(n²) nested loops
  • Column expansion: O(n) algorithm using Map-based column position lookups instead of O(n²) cell iteration
  • Memory efficient: Minimal object creation during traversal

For pages with many matching elements, these optimizations significantly reduce search time.

Development

Running Tests

# Run all tests
npm test

# Run tests in watch mode
npm run test:watch

# Run tests with coverage
npm run test:coverage

Note: The tests/integration/helpers/ folder is excluded from vitest runs as it contains helper utilities r

Linting

npm run lint

Code Coverage

The library includes a Node.js-compatible module (src/element-finder.js) that provides the same functionality as the browser-injected index.js for coverage testing. This module is fully covered by unit tests.

The original index.js is browser-injected code executed via Selenium's executeScript. Coverage for browser-injected code requires browser-based tools like Istanbul or running tests in a browser environment.

License

MIT