Dans les extensions de navigateur et les UI intégrées pour les portails d’entreprise, reproduire le système de design de l’application hôte est difficile. Plutôt que de poursuivre des noms de classes obfusqués ou des styles spécifiques à un framework, cloner des éléments natifs existants et ne remplacer que le nécessaire vous donne une cohérence visuelle “gratuitement.”
Code
interface CloneButtonOptions { /** Sélecteur de l'élément template à cloner */ templateSelector: string /** Nouveau texte à remplacer */ text: string /** Nouvelle icône à remplacer (optionnel) */ icon?: string | HTMLElement /** Gestionnaire de clic */ onClick: (event: MouseEvent) => void}
/** * Cloner un bouton existant de l'application hôte et générer un nouveau bouton */export const cloneNativeButton = ( options: CloneButtonOptions): HTMLElement | null => { const { templateSelector, text, icon, onClick } = options
// Récupérer l'élément template const template = document.querySelector<HTMLElement>(templateSelector) if (!template) { console.warn(`Template not found: ${templateSelector}`) return null }
// Créer un clone profond (excluant les gestionnaires d'événements) const cloned = template.cloneNode(true) as HTMLElement
// Remplacer le nœud de texte const textNode = findTextNode(cloned) if (textNode) { textNode.textContent = text }
// Remplacer l'icône (si spécifié) if (icon) { const iconElement = findIconElement(cloned) if (iconElement) { const newIcon = typeof icon === 'string' ? createIconFromString(icon) : icon iconElement.replaceWith(newIcon) } }
// Les écouteurs d'événements ne sont pas clonés, donc en attacher de nouveaux cloned.addEventListener('click', onClick)
// Supprimer l'ID si présent (éviter les doublons) if (cloned.id) { cloned.removeAttribute('id') }
return cloned}
/** * Trouver le premier nœud de texte dans l'élément */const findTextNode = (element: HTMLElement): Text | null => { const walker = document.createTreeWalker( element, NodeFilter.SHOW_TEXT, { acceptNode: (node) => { // Exclure les nœuds contenant uniquement des espaces return node.textContent?.trim() ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT } } ) return walker.nextNode() as Text | null}
/** * Trouver le premier élément d'icône (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"]') )}
/** * Créer un élément d'icône à partir d'une chaîne HTML */const createIconFromString = (html: string): HTMLElement => { const template = document.createElement('template') template.innerHTML = html.trim() return template.content.firstElementChild as HTMLElement}Utilisation
// Réutiliser le style de bouton existant de GitHubconst myButton = cloneNativeButton({ templateSelector: '.Button--primary', text: 'Deploy', icon: '<svg>...</svg>', onClick: () => { console.log('Deploying...') }})
// Ajouter au DOMdocument.querySelector('.toolbar')?.appendChild(myButton)Comment ça marche
- Utiliser
querySelectorpour récupérer l’élément template à cloner - Créer une copie profonde avec
cloneNode(true)(inclut les éléments enfants) - Parcourir les nœuds de texte avec
TreeWalkeret les remplacer - Trouver les éléments d’icône avec
querySelectoret les remplacer - Les écouteurs d’événements ne sont pas clonés, donc en attacher de nouveaux avec
addEventListener - Supprimer l’attribut
ids’il est présent pour éviter les doublons
Notez que cloneNode préserve les styles, classes et attributs data, mais les écouteurs d’événements ne sont pas copiés.
Avantages
- Cohérence visuelle : Hérite naturellement du système de design de l’application hôte
- Pas de CSS requis : Pas besoin de poursuivre des noms de classes obfusqués ou des styles de framework
- Maintenable : Suit automatiquement les mises à jour du design hôte
- Léger : Implémenté uniquement avec les API DOM, sans dépendances
Mise en garde
cloneNode copie l’état actuel du DOM, donc les classes ou styles qui changent dynamiquement (comme les états de survol) ne sont pas clonés. De plus, si l’élément template est supprimé, cette approche cesse de fonctionner, donc incluez des vérifications d’existence.
Applications
- Ajouter des boutons avec cohérence visuelle dans les extensions de navigateur
- Intégrer des fonctionnalités qui se fondent naturellement dans les systèmes de design complexes des portails d’entreprise
- Insérer des panneaux d’opération personnalisés dans des tableaux de bord tiers
hsb.horse