Como Criar Eventos Personalizados no GTM Sem Precisar Alterar o Código do Site
Element Visibility, cliques com Matches CSS Selector e DOM scraping no GTM — autonomia para mídia sem PR de frontend, e quando isso vira dívida técnica.
Contexto
A campanha sobe amanhã. Você precisa de um evento de clique no CTA, impressão do popup de saída e download do PDF. Engenharia está no backlog do sprint. O ticket de dataLayer.push não sai esta semana.
O Google Tag Manager existe exatamente para esse gap: triggers nativos, seletores CSS e variáveis que leem o DOM sem alterar o repositório do site. Funciona. Também é frágil. Uma troca de classe no redesign e o evento some em silêncio — o Ads continua otimizando como se nada tivesse acontecido.
Este guia é o kit de autonomia para mídia. O padrão de longo prazo continua sendo o contrato de Data Layer. Aqui você aprende o que dá para fazer sozinho — e quando parar.
Problema
| Sintoma | Causa típica |
|---|---|
| Clique no botão “não conta” | Clique caiu no ícone/span dentro do link; trigger só olhava o texto |
| Form envia, GTM calado | AJAX / preventDefault — Form Submission nativo morto |
| Popup de sucesso invisível para analytics | Sem thank-you page; elemento injetado depois do load |
| Evento dispara em lugar errado | Click Classes + contains (falso positivo / ordem de classes) |
| Evento sumiu após redesign | Schema drift: seletor CSS ou ID mudou |
Autonomia sem critério vira dívida: você ganha velocidade agora e perde confiabilidade no próximo deploy de front.
Triggers nativos (sem código do site)
Clique: Just Links vs All Elements
- Just Links — escuta cliques em
<a>. Use Wait for Tags quando a navegação é rápida demais e a tag não chega a disparar. - All Elements — qualquer nó (botão, imagem, SVG,
div). Mais amplo; combine com condição forte de seletor.
Ative as variáveis built-in de clique: Click Text, Click Classes, Click ID, Click URL, Click Element.
Element Visibility
Para banners, popups, mensagem de “obrigado” sem redirect, cards em carrossel:
- Trigger Element Visibility — ID ou CSS Selector.
- Ajuste % mínimo visível (ex.: 50–100%) e tempo mínimo on-screen (ms) para filtrar scroll acidental.
- Frequência: uma vez por página, uma vez por elemento, ou sempre que aparecer.
- Marque Observe DOM changes se o elemento é injetado depois do load (modal, erro de form, toast).
O GTM preenche percent visible e duração — útil como parâmetro do evento no GA4.
Formulário
Form Submission nativo só ajuda quando o submit sobe até o window. Em form AJAX, a ponte sem eng costuma ser: clique no botão de enviar + Element Visibility na mensagem de sucesso. Se o form vive em iFrame ou a resposta é só de rede, vá ao guia Formulários Complexos e iFrames — aí DIY puro não basta.
Matches CSS Selector (não Click Classes contains)
Click Classes devolve uma string ("btn primary cta"). Condicionar com contains “btn” gera falso positivo (nav-button-container) e quebra se a ordem das classes mudar.
O padrão certo: variável Click Element + condição Matches CSS Selector. Isso chama element.matches() no nó real do DOM.
| Seletor | O que faz | Uso típico |
|---|---|---|
button.cta-primary |
Tag button e classe cta-primary |
CTA principal sem pegar link estilizado igual |
#nav-menu a, #nav-menu a * |
Link do menu ou qualquer filho (ícone, span) | Clique no ícone ainda conta como navegação |
a[href$=".pdf"] |
href termina em .pdf |
Download sem listar cada URL |
div#cart > a |
Link filho direto de #cart |
Ação do mini-carrinho, não link dentro da descrição |
[data-product-id] |
Tem atributo data-product-id |
Card de produto com metadado estável |
input[type="checkbox"]:checked |
Checkbox marcado no momento do evento | Aceite de termos / opt-in |
Regra prática: prefira data-* estáveis a classes geradas por CSS-in-JS (css-1a2b3c). Se o front só expõe classe ofuscada, você já está no limite do DIY.
DOM scraping: extrair preço, título, atributo
Só registrar o clique às vezes não basta — você quer item_name ou preço ao lado do botão.
DOM Element Variable
Método: ID ou CSS Selector. Sem nome de atributo → texto (innerText). Com atributo → src, href, data-value, etc.
Custom JavaScript (mínimo)
A variável deve ser uma função anônima com return:
function() {
var el = document.querySelector('h1.product-title');
if (!el) return undefined;
return el.innerText.trim();
}
Para pegar o preço do card clicado (não o primeiro da página), suba a partir do {{Click Element}}:
function() {
var clickEl = {{Click Element}};
if (!clickEl || !clickEl.closest) return undefined;
var card = clickEl.closest('[data-product-id]');
if (!card) return undefined;
var priceEl = card.querySelector('.price, [data-price]');
if (!priceEl) return undefined;
var raw = (priceEl.getAttribute('data-price') || priceEl.innerText || '')
.replace(/[^\d.,]/g, '')
.replace(',', '.');
var n = parseFloat(raw);
return isNaN(n) ? undefined : n;
}
Aviso: isso é tática de campanha, não arquitetura. Trocar .price por .product-cost zera o parâmetro sem alarme. Não trate scrape como fonte da verdade no dataLayer — use só como parâmetro da tag GA4 enquanto o contrato com eng não existe (Data Layer para Devs).
Limites que o DIY não resolve bem
SPA (React/Vue/Angular): um único load → um pageview clássico. O trigger History Change simula pageviews virtuais; no GA4, desative a parte de histórico do Enhanced Measurement se você for medir SPA pelo GTM, senão duplica. Timing: History Change pode disparar antes do DOM novo — título/scrape saem stale. Guia completo de SPA fica para um post dedicado; aqui o ponto é: autonomia tem teto.
Shadow DOM: cliques dentro de Web Components muitas vezes não aparecem em Click Text/Classes/Selector. Se o CTA “não existe” no Preview, pode ser shadow. Aí o caminho é eng expor evento ou dataLayer — não mais um seletor milagroso.
Do trigger ao evento no GA4
- Trigger (Click / Visibility / Custom Event interno).
- Tag GA4 Event — nome em
snake_caseestável (cta_click,file_download,exit_intent_view). EvitectaClick/ nomes de campanha da semana. - Parâmetros:
link_url,file_name,element_text,form_id— o que for estável o bastante para reportar. - Preview (Tag Assistant) → DebugView → só então publicar o container.
Enhanced Measurement já cobre scroll, outbound e file download em muitos sites. Antes de criar tag paralela, confira se você não está duplicando o que o GA4 já coleta.
Checklist de QA
| # | Verificação | Feito quando... |
|---|---|---|
| 1 | Condição | Click Element + Matches CSS Selector (não Classes contains) |
| 2 | Filho do link | Seletor inclui a * quando o clique cai no ícone |
| 3 | Visibility | Observe DOM changes ligado se o elemento é dinâmico |
| 4 | Form AJAX | Clique + Visibility de sucesso — ou fluxo do guia de formulários |
| 5 | Parâmetros | Scrape sanitizado; Number onde for valor; sem PII |
| 6 | Nome GA4 | Evento snake_case reutilizável entre campanhas |
| 7 | Preview | Tag dispara no elemento certo; zero falso positivo na nav |
| 8 | Pós-deploy front | Revalidar seletores após release de UI |
Próximo passo
Use GTM sem código para destravar campanha. Não construa o funil inteiro da empresa em cima de div.css-xyz.
Baixe o guia PDF gratuito (triggers, CSS selectors, checklist de QA): Guia GTM sem alterar o código.
Quando o evento paga mídia ou alimenta Smart Bidding, o próximo passo é um mapa de eventos App/Web: o que o front publica no dataLayer, ownership e QA — para o redesign não apagar sua conversão. Se quiser, partimos do que você já tem no container (CTAs, forms, downloads) e separamos o que pode continuar DIY do que precisa de contrato com engenharia.