You set up an Element Visibility trigger to track Elementor contact form submissions. You configure the CSS selector, enable Observe DOM Changes, set it to fire once per page. You submit the test form. The success message appears on screen.
Nothing fires in Tag Assistant. Just a click event. No visibility trigger, no tag.
You check the trigger settings three times. Everything looks correct. You submit again. Still nothing.
The culprit is almost certainly one missing character in your CSS selector — and GTM gives you no error message whatsoever.
The Root Cause: CSS Selector Syntax
When Elementor displays a form submission success message, the element looks something like this in the DOM:
<div class="elementor-message elementor-message-success">
Your message has been sent.
</div>
Two classes on the same element. To select that element in CSS, you join the classes with no space between them:
.elementor-message.elementor-message-success
The mistake that kills the trigger is adding a space:
.elementor-message elementor-message-success
That space is not harmless formatting. In CSS, a space between selectors is a descendant combinator. It tells the browser: find an element with class elementor-message, then find a descendant of that element that has class elementor-message-success. Those are two separate elements in a parent-child relationship.
That structure does not exist on the page. The selector is syntactically valid CSS — no errors, no warnings. It simply matches nothing. GTM never finds the element, the visibility trigger never fires, and you get no feedback explaining why.
The second part of the broken selector has another issue: elementor-message-success without a leading dot is not a class selector at all. It would be interpreted as an element type selector — like div or span. That element type does not exist in HTML. So the full broken selector is doubly wrong: wrong combinator and missing dot.
The correct selector:
.elementor-message.elementor-message-success
No space. Two dots. Both classes on the same element.
How to Find the Right Selector
Do not guess at class names. Inspect the actual element after a form submission.
Step 1: Submit the form on a non-GTM-preview browser tab. The success message needs to be visible in the DOM while you inspect it.
Step 2: Right-click the success message and choose Inspect. The DevTools Elements panel opens with the success div highlighted. You will see the full class list on the element — in Elementor this is typically elementor-message elementor-message-success on a single div.
Step 3: Construct the selector manually. For every class on the element, add a dot and the class name, with no spaces between them. So class="elementor-message elementor-message-success" becomes .elementor-message.elementor-message-success.
Step 4: Verify the selector in the console before touching GTM. Open the DevTools Console tab and run:
document.querySelector('.elementor-message.elementor-message-success')
If the selector is correct, the console returns the element object. If it returns null, the selector does not match anything currently in the DOM. At that point you know the selector is wrong, or the element has already been removed from the DOM after you switched tabs.
Step 5: Test with the element visible. Keep the success message on screen while you run the console check. Some implementations remove the success div after a few seconds. If that happens and you run querySelector after it disappears, you will get null even with a correct selector.
Once document.querySelector() returns the element, copy that exact selector string into your GTM trigger. Do not retype it.
Secondary Reasons Visibility Triggers Fail on Injected Elements
Getting the selector right is the most common fix, but there are four other failure modes worth knowing.
The element is hidden with opacity or transform instead of display: none
GTM’s Element Visibility trigger works by checking whether an element meets a minimum visibility threshold — by default, 1% of the element must be within the viewport. It uses the Intersection Observer API under the hood.
The complication: some Elementor themes and form plugins hide the success message with opacity: 0 or transform: translateY(-20px) rather than display: none. An element with opacity: 0 is technically in the viewport and technically taking up space — depending on timing, GTM may fire the visibility trigger on the hidden state, not when the message becomes visible to the user. Or it may fire immediately when the element is injected, before the CSS transition completes.
If your tag fires but the conversion is recorded before the user sees the confirmation, inspect the element’s initial CSS state. Look for opacity, visibility: hidden, or transform-based hiding. If those are present, the visibility trigger is not the right tool — move to the MutationObserver approach described at the end of this post.
Once per page suppressing fires during active testing
If your trigger is set to fire once per page, it will suppress after the first fire in a session. During testing, this means the trigger silently stops firing after your first successful form submission, even in Tag Assistant.
To test reliably, reload the page between each test submission. This resets the once-per-page counter. If you submit the form multiple times without reloading, the trigger fires on the first submission only — which can make it look like it randomly stopped working.
Observe DOM Changes not saving correctly
The Observe DOM Changes checkbox in the Element Visibility trigger settings tells GTM to watch for the element being added to the DOM after the page loads. Without it, GTM only checks for the element at the time the trigger initializes — which means it will never find a success message that does not exist until after form submission.
This setting needs to be enabled. After you save the trigger, go back and re-open it to confirm the checkbox is still checked. This is worth double-checking because in some GTM versions, certain settings do not persist on save if the trigger has validation issues elsewhere.
The element is not in the DOM until after submission
This is not a misconfiguration — it is how Elementor forms work. The success div is injected into the DOM via JavaScript after the form submits. It does not exist when the page first loads.
This is exactly what Observe DOM Changes is designed to handle. When the checkbox is enabled, GTM sets up a MutationObserver on the page’s document body and watches for the target element to be added. When it appears, GTM evaluates whether it is visible and fires accordingly.
If Observe DOM Changes is disabled, the trigger evaluates once on page load, finds nothing, and gives up. It will never fire regardless of how correct the selector is.
Testing Checklist in Tag Assistant
Tag Assistant behavior with visibility triggers has a few quirks that can mislead you.
Step 1: Open your site in GTM Preview mode. Submit the form. Watch the event list in the left panel for a new event to appear after the click event.
Step 2: Look for a visibility event in the event stream. If the trigger fires correctly, you will see an event named something like Scroll Depth or Element Visibility in the stream. Click it.
Step 3: In the event detail, check the Tags Fired panel. Your tag should appear here. If it does not appear, check the Tags Not Fired panel and click the tag to see which condition failed.
Step 4: If no visibility event appears at all, the trigger never evaluated. This means either the selector matched nothing, the element was never added to the DOM, or Observe DOM Changes is disabled.
Step 5: Confirm the element exists post-submission using the console. After submitting the form, while the success message is visible, run this in the DevTools console:
document.querySelector('.elementor-message.elementor-message-success')
If this returns null, the element is not in the DOM — which means either the form submission failed silently, or the element was removed before you checked. If it returns an element, the issue is in GTM’s trigger configuration, not the selector.
Step 6: Check element visibility directly. With the element returned by querySelector, check its bounding rect:
document.querySelector('.elementor-message.elementor-message-success').getBoundingClientRect()
If the returned height and width are both 0, the element is in the DOM but has no visible dimensions. GTM will not consider it visible regardless of the selector. This can happen if the element is rendered inside a container with overflow: hidden or height: 0.
When to Use a MutationObserver Instead
The Element Visibility trigger handles the majority of Elementor form tracking cases once the selector is correct and Observe DOM Changes is enabled. For most sites, that is all you need.
There are cases where it is not reliable enough. If the success message appears and disappears quickly (some Elementor setups auto-dismiss after a few seconds), the visibility trigger may not evaluate before the element is removed. If the element is hidden via opacity transitions, the timing mismatch described earlier can cause misfires.
In those cases, a Custom HTML tag with a MutationObserver is more robust. Instead of waiting for the element to become visible, you watch for it to be added to the DOM at all:
<script>
(function() {
var observed = false;
var observer = new MutationObserver(function(mutations) {
var i, j, node;
if (observed) { return; }
for (i = 0; i < mutations.length; i++) {
for (j = 0; j < mutations[i].addedNodes.length; j++) {
node = mutations[i].addedNodes[j];
if (
node.nodeType === 1 &&
node.classList &&
node.classList.contains('elementor-message-success')
) {
observed = true;
observer.disconnect();
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ event: 'form_success' });
}
}
}
});
observer.observe(document.body, { childList: true, subtree: true });
})();
</script>
This fires a form_success DataLayer event the moment the success element is added to the DOM, regardless of its visibility state or how quickly it disappears. The observed flag prevents it from firing more than once per page load.
Fire this tag on All Pages, and create a Custom Event trigger in GTM listening for form_success to use as the actual conversion trigger.
This approach is more code and harder to audit in GTM’s interface, but it eliminates the timing and visibility state issues that can make the built-in trigger unreliable. Use it when the visibility trigger approach keeps misfiring despite a correct selector and proper configuration.
Summary
The most common reason an Elementor form visibility trigger never fires is a space in the CSS selector where there should not be one. .elementor-message .elementor-message-success selects a descendant element that does not exist. .elementor-message.elementor-message-success selects the actual element. One character difference, no error message, complete failure.
Before adjusting anything else: verify your selector with document.querySelector() in the console while the success message is visible. If that returns an element, your GTM trigger will work once it is correctly configured. If it returns null, the selector is the problem.
Beyond the selector, check that Observe DOM Changes is enabled, reload between test submissions to reset the once-per-page counter, and inspect the element’s CSS if you suspect it is being hidden via opacity rather than display.
For cases where timing makes the visibility trigger unreliable, the MutationObserver approach in a Custom HTML tag gives you deterministic firing at the moment the element is injected — no visibility threshold, no timing window to miss.
Related Posts
GTM Tag Sequencing and Tag Firing Priority: How to Control the Order Everything Executes
12 min read
Tracking a Next.js SPA in GTM: History Navigation, Invisible Carts, and Full E-Commerce Setup
When Preview Mode Lies: Runtime-Level HTTP Logging for Server-Side GTM
Need Help With Your Google Ads?
I help e-commerce brands scale profitably with data-driven PPC strategies.
Get In Touch