Skip to content

salesforce/kagekiri

kagekiri

Shadow DOM-piercing query APIs. Supports:

API Interface Support
querySelector Element, Document βœ…
querySelectorAll Element, Document βœ…
getElementsByClassName Element, Document βœ…
getElementsByTagName Element, Document βœ…
getElementsByTagNameNS Element, Document βœ…
getElementById Document βœ…
getElementsByName Document βœ…
matches Element βœ…
closest Element βœ…

Usage

Install:

npm install --save kagekiri

Query the document or a specific element:

import { querySelector, querySelectorAll } from 'kagekiri'

// query the document
const elements = querySelectorAll('.container button')
const element = querySelector('.container button')

// or a specific element
const elements = querySelectorAll('button', otherElement)
const element = querySelector('button', otherElement)

Example

A custom element:

<my-component></my-component>
<script>
  class MyComponent extends HTMLElement {
    constructor() {
      super()
      const shadowRoot = this.attachShadow({mode: 'open'})
      shadowRoot.innerHTML = '<span class="hello">Hello</span>'
    }
  }
  customElements.define('my-component', MyComponent)
</script>

It renders as:

<my-component>
  <!-- shadow root (open) -->
  <span class="hello">Hello</span>
</my-component>

You can't query the .hello element:

document.querySelector('.hello')    // undefined 😞
document.querySelectorAll('.hello') // empty 😞

But with kagekiri you can!

kagekiri.querySelector('.hello')    // <span> πŸ˜ƒ
kagekiri.querySelectorAll('.hello') // [<span>] πŸ˜ƒ

Your can even query across the shadow boundary!

kagekiri.querySelector('my-component .hello')   // <span> πŸ˜ƒ
kagekiri.querySelector('my-component > .hello') // <span> πŸ˜ƒ

API

closest

β–Έ closest(selector: string, element: Node): Element | null

Find the closest ancestor of an element (or the element itself) matching the given CSS selector. Analogous to Element.closest

Parameters:

Name Type Description
selector string CSS selector
element Node target element to match against, and whose ancestors to match against

Returns: Element | null


getElementById

β–Έ getElementById(id: string, context?: DocumentOrShadowRoot): Element | null

Query for an element matching the given ID, or null if not found. Analogous to Document.getElementById

The default context is document. Choose another DocumentOrShadowRoot to query within that context.

Parameters:

Name Type Description
id string element ID
context? DocumentOrShadowRoot context to query in, or document by default

Returns: Element | null


getElementsByClassName

β–Έ getElementsByClassName(names: string, context?: Node): Element[]

Query for all elements matching a given class name, or multiple if a whitespace-separated list is provided. Analogous to Document.getElementsByClassName.

Unlike the standard API, this returns a static array of Elements rather than a live HTMLCollection.

The default context is document. Choose another node to query within that context.

Parameters:

Name Type Description
names string class name or whitespace-separated class names
context? Node context to query in, or document by default

Returns: Element[]


getElementsByName

β–Έ getElementsByName(name: string, context?: DocumentOrShadowRoot): Element[]

Query for all elements matching a given name. Analogous to Document.getElementsByName

The default context is document. Choose another DocumentOrShadowRoot to query within that context.

Unlike the standard API, this returns a static array of Elements rather than a live NodeList.

Parameters:

Name Type Description
name string element name attribute
context? DocumentOrShadowRoot context to query in, or document by default

Returns: Element[]


getElementsByTagName

β–Έ getElementsByTagName(tagName: string, context?: Node): Element[]

Query for all elements matching a given tag name. Analogous to Document.getElementsByTagName. The "*" query is supported.

Unlike the standard API, this returns a static array of Elements rather than a live HTMLCollection.

The default context is document. Choose another node to query within that context.

Parameters:

Name Type Description
tagName string name of the element tag
context? Node context to query in, or document by default

Returns: Element[]


getElementsByTagNameNS

β–Έ getElementsByTagNameNS(namespaceURI: string, localName: string, context?: Node): Element[]

Query for all elements matching a given tag name and namespace. Analogous to Document.getElementsByTagNameNS. The "*" query is supported.

Unlike the standard API, this returns a static array of Elements rather than a live NodeList.

The default context is document. Choose another node to query within that context.

Parameters:

Name Type Description
namespaceURI string namespace URI, or "*" for all
localName string local name, or "*" for all
context? Node context to query in, or document by default

Returns: Element[]


matches

β–Έ matches(selector: string, element: Node): boolean

Return true if the given Node matches the given CSS selector, or false otherwise. Analogous to Element.closest

Parameters:

Name Type Description
selector string CSS selector
element Node element to match against

Returns: boolean


querySelector

β–Έ querySelector(selector: string, context?: Node): Element | null

Query for a single element matching the CSS selector, or return null if not found. Analogous to Document.querySelector

The default context is document. Choose another element or DocumentOrShadowRoot to query within that context.

Parameters:

Name Type Description
selector string CSS selector
context? Node context to query in, or document by default

Returns: Element | null


querySelectorAll

β–Έ querySelectorAll(selector: string, context?: Node): Element[]

Query for all elements matching a CSS selector. Analogous to Document.querySelectorAll

The default context is document. Choose another node to query within that context.

Parameters:

Name Type Description
selector string CSS selector
context? Node context to query in, or document by default

Returns: Element[]

How it works

kagekiri parses the CSS selector using postcss-selector-parser. Then it queries the entire DOM tree, traverses any shadowRoots it may find, and checks the selector from children to ancestors (the same way a browser would).

Note that it only works on open shadow DOM. Closed shadow DOM cannot be traversed.

Slotted elements are considered to be children of their slots (inside the shadow DOM) rather than children of their host components. If you don't want this behavior, you can use the normal DOM APIs (e.g. document.querySelector() or document.querySelectorAll()).

See the tests for full supported CSS features.

The name

ι™° kage (shadow) + εˆ‡γ‚Š kiri (cut).

Roughly, "shadow-cutter."

Build

npm run build

Build TypeScript-based API docs using kagekiri.d.ts, inject them into the README:

npm run typedoc

Test

npm test

Debug:

npm run test:debug

Test with coverage:

npm run test:coverage

Lint:

npm run lint

Fix most lint issues:

npm run lint:fix

Test bundle size:

npm run test:bundlesize