Home

Awesome

kagekiri

Shadow DOM-piercing query APIs. Supports:

APIInterfaceSupport
querySelectorElement, Document
querySelectorAllElement, Document
getElementsByClassNameElement, Document
getElementsByTagNameElement, Document
getElementsByTagNameNSElement, Document
getElementByIdDocument
getElementsByNameDocument
matchesElement
closestElement

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:

NameTypeDescription
selectorstringCSS selector
elementNodetarget 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:

NameTypeDescription
idstringelement ID
context?DocumentOrShadowRootcontext 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:

NameTypeDescription
namesstringclass name or whitespace-separated class names
context?Nodecontext 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:

NameTypeDescription
namestringelement name attribute
context?DocumentOrShadowRootcontext 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:

NameTypeDescription
tagNamestringname of the element tag
context?Nodecontext 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:

NameTypeDescription
namespaceURIstringnamespace URI, or "*" for all
localNamestringlocal name, or "*" for all
context?Nodecontext 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:

NameTypeDescription
selectorstringCSS selector
elementNodeelement 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:

NameTypeDescription
selectorstringCSS selector
context?Nodecontext 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:

NameTypeDescription
selectorstringCSS selector
context?Nodecontext 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