🧩
Seanime Extensions
🧩
Seanime Extensions
  • Seanime
    • Getting started
    • Core APIs
    • Changelog
  • Content Providers
    • Write, test, share
    • Anime torrent provider
    • Manga provider
    • Online streaming provider
  • Plugins
    • Introduction
    • Write, test, share
    • APIs
      • Helpers
      • Store
      • Storage
      • Database
      • AniList
      • System
        • Permissions
        • OS
        • Filepath
        • Commands
        • Buffers, I/O
        • MIME
    • UI
      • Basics
      • User Interface
        • Tray
        • Toast
        • Screen
        • Command Palette
        • Action
        • DOM
      • Anime/Library
        • Anime
        • Playback
        • Continuity
        • Auto Downloader
        • Auto Scanner
        • Filler Manager
        • External Player Link
      • Downloading
        • Downloader
        • Torrent Client
      • Other
        • Manga
        • Discord
        • MPV
    • Hooks
    • Example
  • Frequently asked
    • Feature requests
Powered by GitBook
On this page
  • Core Methods
  • onReady
  • query
  • queryOne
  • observe
  • createElement
  • asElement
  • DOM Element Methods
  • Performance Best Practices
  1. Plugins
  2. UI
  3. User Interface

DOM

API for DOM manipulation in Seanime plugins. Each DOM operation involves communication between the plugin and the browser, so understanding performance considerations is important.

Pitfall

Users having multiple tabs open can lead to unexpected behavior.

This happens because DOM manipulation events are broadcasted to all connected clients.

Core Methods

onReady

Executes a callback when the DOM is ready.

Parameters:

  • callback: Function to execute

Example:

ctx.dom.onReady(() => {
  console.log("DOM is ready...")
})

query

Queries the DOM for elements matching the selector.

Parameters:

  • selector: CSS selector string

  • options: (Optional)

    • withInnerHTML: Boolean - Include the innerHTML property in the matched elements

    • identifyChildren: Boolean - Assign IDs to all child elements

Returns: Promise<DOMElement[]>

Example:

// Get all episode cards
const episodeCards = await ctx.dom.query("[data-episode-card]", { withInnerHTML: true })

queryOne

Queries the DOM for a single element matching the selector.

Parameters:

  • selector: CSS selector string

  • options: Same as query()

Returns: Promise<DOMElement | null>

Example:

// Get the main container
const mainContainer = await ctx.dom.queryOne("#main-container")

// Get user profile with inner HTML
const userProfile = await ctx.dom.queryOne(".user-profile", { withInnerHTML: true })

observe

Observes changes to the DOM for elements matching the selector. Returns functions to stop observing and to manually refetch elements.

Parameters:

  • selector: CSS selector string

  • callback: Function(elements: DOMElement[]) => void

  • options: Same as query()

Returns: [stopObserving: () => void, refetch: () => void]

Example:

// Observe when new content cards are added to the page
const [stopObserving, refetchCards] = ctx.dom.observe(".content-card", async (cards) => {
  console.log(`Found ${cards.length} content cards`)
  
  // Process each card
  for (const card of cards) {
    const title = await card.queryOne(".card-title")
    if (title) {
      console.log(`Card title: ${await title.getText()}`)
    }
  }
})

// Later, to stop observing:
stopObserving()

// To manually trigger a refresh:
refetchCards()

createElement

Creates a new DOM element.

Parameters:

  • tagName: HTML tag name

Returns: Promise

Example:

// Create a new div element
const newDiv = await ctx.dom.createElement("div")
newDiv.setAttribute("class", "custom-element")
newDiv.setText("This is a dynamically created element")

// Create a button
const newButton = await ctx.dom.createElement("button")
newButton.setText("Click me")

asElement

Returns a DOM element object from an element ID. Useful when using identifyChildren option.

Parameters:

  • elementId: String ID of the element

Returns: DOMElement

Example:

// When using with identifyChildren
const container = await ctx.dom.queryOne("#container", { 
  withInnerHTML: true, 
  identifyChildren: true 
})

// Parse HTML locally
const $ = LoadDoc(container.innerHTML)
const buttonId = $(".action-button").attr("id")

// Get a reference to the actual DOM element
const button = ctx.dom.asElement(buttonId)
button.setText("New Button Text")

DOM Element Methods

Content Methods

getText()

Gets the text content of the element.

Returns: Promise

Example:

const titleElement = await ctx.dom.queryOne(".title")
const titleText = await titleElement.getText()
console.log(`Title: ${titleText}`)

setText(text)

Sets the text content of the element.

Parameters:

  • text: String to set as text content

Example:

const statusElement = await ctx.dom.queryOne(".status")
statusElement.setText("Active")

getAttribute(name)

Gets the value of an attribute.

Parameters:

  • name: Attribute name

Returns: Promise<string | null>

Example:

const link = await ctx.dom.queryOne("a.resource-link")
const href = await link.getAttribute("href")
console.log(`Resource URL: ${href}`)

getAttributes()

Gets all attributes of the element.

Returns: Promise<Record<string, string>>

Example:

const image = await ctx.dom.queryOne("img.poster")
const attributes = await image.getAttributes()
console.log(`Image src: ${attributes.src}`)
console.log(`Image alt: ${attributes.alt}`)

setAttribute(name, value)

Sets the value of an attribute.

Parameters:

  • name: Attribute name

  • value: Attribute value

Example:

const image = await ctx.dom.queryOne(".thumbnail")
image.setAttribute("src", "https://example.com/new-image.jpg")
image.setAttribute("alt", "Updated thumbnail image")

removeAttribute(name)

Removes an attribute.

Parameters:

  • name: Attribute name

Example:

const button = await ctx.dom.queryOne(".disabled-button")
button.removeAttribute("disabled")

hasAttribute(name)

Checks if the element has an attribute.

Parameters:

  • name: Attribute name

Returns: Promise

Example:

const form = await ctx.dom.queryOne("form")
const isSubmitted = await form.hasAttribute("data-submitted")
if (isSubmitted) {
  console.log("Form was already submitted")
}

Style Methods

setStyle(property, value)

Sets a style property on the element.

Parameters:

  • property: CSS property name

  • value: Property value

Example:

const spoilerText = await ctx.dom.queryOne(".spoiler")
spoilerText.setStyle("filter", "blur(5px)")
spoilerText.setStyle("cursor", "pointer")

getStyle(property?)

Gets the style of the element.

Parameters:

  • property: (Optional) Property name

Returns: Promise<string | Record<string, string>>

Example:

const element = await ctx.dom.queryOne(".styled-element")
// Get single property
const color = await element.getStyle("color")
// Get all styles
const allStyles = await element.getStyle()

removeStyle(property)

Removes a style property.

Parameters:

  • property: CSS property name

Example:

const spoilerText = await ctx.dom.queryOne(".spoiler")
// Remove blur effect when clicked
spoilerText.removeStyle("filter")

hasStyle(property)

Checks if the element has a style property set.

Parameters:

  • property: CSS property name

Returns: Promise

Example:

const element = await ctx.dom.queryOne(".target")
const hasTransition = await element.hasStyle("transition")
if (!hasTransition) {
  element.setStyle("transition", "opacity 0.3s ease")
}

getComputedStyle(property)

Gets the computed style of the element.

Parameters:

  • property: CSS property name

Returns: Promise

Example:

const box = await ctx.dom.queryOne(".box")
const actualWidth = await box.getComputedStyle("width")
console.log(`Box actual width: ${actualWidth}`)

CSS Class Methods

addClass(className)

Adds a class to the element.

Parameters:

  • className: CSS class name

Example:

const card = await ctx.dom.queryOne(".card")
card.addClass("highlighted")
card.addClass("selected")

hasClass(className)

Checks if the element has a class.

Parameters:

  • className: CSS class name

Returns: Promise

Example:

const row = await ctx.dom.queryOne("tr")
const isActive = await row.hasClass("active")
if (!isActive) {
  row.addClass("active")
}

DOM Traversal and Manipulation

append(child)

Appends a child to the element.

Parameters:

  • child: DOMElement to append

Example:

const container = await ctx.dom.queryOne(".container")
const newElement = await ctx.dom.createElement("div")
newElement.setText("New child element")
container.append(newElement)

before(sibling)

Inserts a sibling before the element.

Parameters:

  • sibling: DOMElement to insert

Example:

const referenceElement = await ctx.dom.queryOne(".reference")
const newElement = await ctx.dom.createElement("div")
newElement.setText("Inserted before reference")
referenceElement.before(newElement)

after(sibling)

Inserts a sibling after the element.

Parameters:

  • sibling: DOMElement to insert

Example:

const referenceElement = await ctx.dom.queryOne(".reference")
const newElement = await ctx.dom.createElement("div")
newElement.setText("Inserted after reference")
referenceElement.after(newElement)

remove()

Removes the element from the DOM.

Example:

const outdatedNotice = await ctx.dom.queryOne(".outdated-notice")
if (outdatedNotice) {
  outdatedNotice.remove()
}

getParent(opts?)

Gets the parent of the element.

Parameters:

  • opts: (Optional) Same options as query()

Returns: Promise<DOMElement | null>

Example:

const listItem = await ctx.dom.queryOne("li.active")
const list = await listItem.getParent()
console.log(`Parent element tag: ${list.tagName}`)

getChildren(opts?)

Gets the children of the element.

Parameters:

  • opts: (Optional) Same options as query()

Returns: Promise<DOMElement[]>

Example:

const list = await ctx.dom.queryOne("ul.menu")
const listItems = await list.getChildren()
console.log(`Menu has ${listItems.length} items`)

query(selector)

Queries the DOM for elements that are descendants of this element and match the selector.

Parameters:

  • selector: CSS selector string

Returns: Promise<DOMElement[]>

Example:

const articleBody = await ctx.dom.queryOne("article.blog-post")
const paragraphs = await articleBody.query("p")
console.log(`Article has ${paragraphs.length} paragraphs`)

queryOne(selector)

Queries the DOM for a single element that is a descendant of this element and matches the selector.

Parameters:

  • selector: CSS selector string

Returns: Promise<DOMElement | null>

Example:

const card = await ctx.dom.queryOne(".card")
const title = await card.queryOne(".card-title")
const description = await card.queryOne(".card-description")

addEventListener(event, callback)

Adds an event listener to the element.

Parameters:

  • event: Event name (e.g., "click")

  • callback: Function to call when the event occurs

Returns: Function to remove the event listener

Example:

const button = await ctx.dom.queryOne(".action-button")
const removeListener = button.addEventListener("click", (event) => {
  console.log("Button clicked!")
})

// Later, to remove the listener:
removeListener()

Performance Best Practices

Minimize Roundtrips

Each async DOM method call represents a roundtrip between your plugin (on the server) and the browser. Minimize these for better performance.

Inefficient:

// ❌ Multiple sequential roundtrips
const items = await ctx.dom.query(".item")

for (const item of items) {
  const title = await item.queryOne(".title")
  const description = await item.queryOne(".description")
  const image = await item.queryOne("img")
  
  if (title) await title.setText("New Title")
  if (description) await description.setText("New Description")
}

Efficient:

// ✅ Get all data at once, process locally
const items = await ctx.dom.query(".item", { 
  withInnerHTML: true, 
  identifyChildren: true 
})

for (const item of items) {
  const $ = LoadDoc(item.innerHTML)
  
  // Access elements without additional roundtrips
  const titleId = $(".title").attr("id")
  const descriptionId = $(".description").attr("id")
  const imageId = $("img").attr("id")
  
  // Now make direct modifications
  if (titleId) ctx.dom.asElement(titleId).setText("New Title")
  if (descriptionId) ctx.dom.asElement(descriptionId).setText("New Description")
}

Use observe() Efficiently

When using observe(), apply performance optimizations to handle elements efficiently.

Example:

const [stopObserving, refetch] = ctx.dom.observe(".dynamic-content", async (elements) => {
  // Use withInnerHTML and identifyChildren for efficient processing
  for (const element of elements) {
    const $ = LoadDoc(element.innerHTML)
    
    // Process locally first
    const buttonIds = $("button").map((i, el) => $(el).attr("id")).get()
    
    // Then make direct DOM updates
    for (const buttonId of buttonIds) {
      if (buttonId) {
        const button = ctx.dom.asElement(buttonId)
        button.addEventListener("click", handleButtonClick)
      }
    }
  }
}, { withInnerHTML: true, identifyChildren: true })
PreviousActionNextAnime/Library

Last updated 24 days ago