Em extensões de navegador e UIs incorporadas para portais corporativos, replicar o sistema de design da aplicação host é desafiador. Em vez de perseguir nomes de classes ofuscados ou estilos específicos de frameworks, clonar elementos nativos existentes e substituir apenas o necessário proporciona consistência visual “gratuitamente”.
Código
interface CloneButtonOptions { /** Seletor para o elemento template a ser clonado */ templateSelector: string /** Novo texto para substituir */ text: string /** Novo ícone para substituir (opcional) */ icon?: string | HTMLElement /** Manipulador de clique */ onClick: (event: MouseEvent) => void}
/** * Clonar um botão existente da aplicação host e gerar um novo botão */export const cloneNativeButton = ( options: CloneButtonOptions): HTMLElement | null => { const { templateSelector, text, icon, onClick } = options
// Obter o elemento template const template = document.querySelector<HTMLElement>(templateSelector) if (!template) { console.warn(`Template not found: ${templateSelector}`) return null }
// Criar um clone profundo (excluindo manipuladores de eventos) const cloned = template.cloneNode(true) as HTMLElement
// Substituir nó de texto const textNode = findTextNode(cloned) if (textNode) { textNode.textContent = text }
// Substituir ícone (se especificado) if (icon) { const iconElement = findIconElement(cloned) if (iconElement) { const newIcon = typeof icon === 'string' ? createIconFromString(icon) : icon iconElement.replaceWith(newIcon) } }
// Ouvintes de eventos não são clonados, então anexar novos cloned.addEventListener('click', onClick)
// Remover ID se presente (evitar duplicatas) if (cloned.id) { cloned.removeAttribute('id') }
return cloned}
/** * Encontrar o primeiro nó de texto dentro do elemento */const findTextNode = (element: HTMLElement): Text | null => { const walker = document.createTreeWalker( element, NodeFilter.SHOW_TEXT, { acceptNode: (node) => { // Excluir nós apenas com espaços em branco return node.textContent?.trim() ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT } } ) return walker.nextNode() as Text | null}
/** * Encontrar o primeiro elemento de ícone (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"]') )}
/** * Criar um elemento de ícone a partir de uma string HTML */const createIconFromString = (html: string): HTMLElement => { const template = document.createElement('template') template.innerHTML = html.trim() return template.content.firstElementChild as HTMLElement}Uso
// Reutilizar o estilo de botão existente do GitHubconst myButton = cloneNativeButton({ templateSelector: '.Button--primary', text: 'Deploy', icon: '<svg>...</svg>', onClick: () => { console.log('Deploying...') }})
// Adicionar ao DOMdocument.querySelector('.toolbar')?.appendChild(myButton)Como Funciona
- Usar
querySelectorpara obter o elemento template a ser clonado - Criar uma cópia profunda com
cloneNode(true)(inclui elementos filhos) - Percorrer nós de texto com
TreeWalkere substituí-los - Encontrar elementos de ícone com
querySelectore substituí-los - Ouvintes de eventos não são clonados, então anexar novos com
addEventListener - Remover o atributo
idse presente para evitar duplicatas
Note que cloneNode preserva estilos, classes e atributos data, mas ouvintes de eventos não são copiados.
Benefícios
- Consistência Visual: Herda naturalmente o sistema de design da aplicação host
- CSS Não Necessário: Não é preciso perseguir nomes de classes ofuscados ou estilos de framework
- Manutenível: Segue automaticamente atualizações de design do host
- Leve: Implementado apenas com APIs DOM, sem dependências
Ressalvas
cloneNode copia o estado atual do DOM, então classes ou estilos que mudam dinamicamente (como estados de hover) não são clonados. Além disso, se o elemento template for removido, essa abordagem para de funcionar, então inclua verificações de existência.
Aplicações
- Adicionar botões com consistência visual em extensões de navegador
- Incorporar funcionalidades que se mesclam naturalmente em sistemas de design complexos de portais corporativos
- Inserir painéis de operação personalizados em dashboards de terceiros
hsb.horse