logo hsb.horse
← Zur Snippets-Übersicht

Snippets

Native UI-Elemente klonen, um Stile zu erben

Ein leichtgewichtiges Muster, das visuelle Konsistenz durch Klonen bestehender Host-App-Buttons mit cloneNode gewährleistet, wobei nur Icon und Text ersetzt werden. Kein Kampf mit obfuskiertem CSS nötig.

Veröffentlicht: Aktualisiert:

In Browser-Erweiterungen und eingebetteten UIs für Unternehmensportale ist es schwierig, das Design-System der Host-App nachzubilden. Anstatt obfuskierte Klassennamen oder Framework-spezifische Stile zu verfolgen, erhält man durch Klonen bestehender nativer Elemente und Ersetzen nur des Notwendigen visuelle Konsistenz „kostenlos”.

Code

interface CloneButtonOptions {
/** Selektor für das zu klonende Template-Element */
templateSelector: string
/** Neuer Text zum Ersetzen */
text: string
/** Neues Icon zum Ersetzen (optional) */
icon?: string | HTMLElement
/** Click-Handler */
onClick: (event: MouseEvent) => void
}
/**
* Einen bestehenden Button der Host-App klonen und einen neuen Button generieren
*/
export const cloneNativeButton = (
options: CloneButtonOptions
): HTMLElement | null => {
const { templateSelector, text, icon, onClick } = options
// Template-Element abrufen
const template = document.querySelector<HTMLElement>(templateSelector)
if (!template) {
console.warn(`Template not found: ${templateSelector}`)
return null
}
// Tiefen Klon erstellen (ohne Event-Handler)
const cloned = template.cloneNode(true) as HTMLElement
// Textknoten ersetzen
const textNode = findTextNode(cloned)
if (textNode) {
textNode.textContent = text
}
// Icon ersetzen (falls angegeben)
if (icon) {
const iconElement = findIconElement(cloned)
if (iconElement) {
const newIcon = typeof icon === 'string'
? createIconFromString(icon)
: icon
iconElement.replaceWith(newIcon)
}
}
// Event-Listener werden nicht geklont, also neue anhängen
cloned.addEventListener('click', onClick)
// ID entfernen, falls vorhanden (Duplikate vermeiden)
if (cloned.id) {
cloned.removeAttribute('id')
}
return cloned
}
/**
* Den ersten Textknoten innerhalb des Elements finden
*/
const findTextNode = (element: HTMLElement): Text | null => {
const walker = document.createTreeWalker(
element,
NodeFilter.SHOW_TEXT,
{
acceptNode: (node) => {
// Nur-Leerzeichen-Knoten ausschließen
return node.textContent?.trim()
? NodeFilter.FILTER_ACCEPT
: NodeFilter.FILTER_REJECT
}
}
)
return walker.nextNode() as Text | null
}
/**
* Das erste Icon-Element finden (svg, img, i, etc.)
*/
const findIconElement = (element: HTMLElement): Element | null => {
return (
element.querySelector('svg') ||
element.querySelector('img') ||
element.querySelector('i[class*="icon"]') ||
element.querySelector('[role="img"]')
)
}
/**
* Ein Icon-Element aus einem HTML-String erstellen
*/
const createIconFromString = (html: string): HTMLElement => {
const template = document.createElement('template')
template.innerHTML = html.trim()
return template.content.firstElementChild as HTMLElement
}

Verwendung

// GitHubs bestehenden Button-Stil wiederverwenden
const myButton = cloneNativeButton({
templateSelector: '.Button--primary',
text: 'Deploy',
icon: '<svg>...</svg>',
onClick: () => {
console.log('Deploying...')
}
})
// Zum DOM hinzufügen
document.querySelector('.toolbar')?.appendChild(myButton)

Funktionsweise

  1. querySelector verwenden, um das zu klonende Template-Element abzurufen
  2. Eine tiefe Kopie mit cloneNode(true) erstellen (inkl. Kindelemente)
  3. Textknoten mit TreeWalker durchlaufen und ersetzen
  4. Icon-Elemente mit querySelector finden und ersetzen
  5. Event-Listener werden nicht geklont, also neue mit addEventListener anhängen
  6. Das id-Attribut entfernen, falls vorhanden, um Duplikate zu vermeiden

Beachten Sie, dass cloneNode Stile, Klassen und Data-Attribute bewahrt, Event-Listener jedoch nicht kopiert werden.

Vorteile

  • Visuelle Konsistenz: Erbt natürlich das Design-System der Host-App
  • Kein CSS erforderlich: Keine Notwendigkeit, obfuskierte Klassennamen oder Framework-Stile zu verfolgen
  • Wartbar: Folgt automatisch Design-Updates des Hosts
  • Leichtgewichtig: Nur mit DOM-APIs implementiert, keine Abhängigkeiten

Einschränkungen

cloneNode kopiert den aktuellen Zustand des DOM, sodass sich dynamisch ändernde Klassen oder Stile (wie Hover-Zustände) nicht geklont werden. Wenn das Template-Element entfernt wird, funktioniert dieser Ansatz nicht mehr. Daher sollten Existenzprüfungen eingebaut werden.

Anwendungsfälle

  • Buttons mit visueller Konsistenz in Browser-Erweiterungen hinzufügen
  • Features einbetten, die sich natürlich in komplexe Design-Systeme von Unternehmensportalen einfügen
  • Benutzerdefinierte Bedienpanels in Drittanbieter-Dashboards einfügen