craft-seomatic
craft-seomatic copied to clipboard
Positioning of GTM code
Question
Hi there,
I am trying to control the source order of the GTM script element in the
section of my template. It appears that SEOmatic always injects right before the closing</head>
tag.
I am trying to do this to implement the CookieBot auto mode along with Google Consent Mode and GTM, which requires a specific source order:
Ensure that the following scripts are the first on the page to load, in this exact order:
- Google Consent Mode
- Google Tag Manager
- Cookiebot CMP (with automatic cookie blocking)
This is my current code:
{#
GTM, CookieBot and Google Consent Mode implementation
Note: The source order is important!
#}
{# Enable Google Consent Mode #}
<script data-cookieconsent="ignore">
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag("consent", "default", {
ad_personalization: "denied",
ad_storage: "denied",
ad_user_data: "denied",
analytics_storage: "denied",
functionality_storage: "denied",
personalization_storage: "denied",
security_storage: "granted",
wait_for_update: 500,
});
gtag("set", "ads_data_redaction", true);
gtag("set", "url_passthrough", true);
</script>
{# Force rendering of GTM script in test environment #}
{% if getenv('CRAFT_ENVIRONMENT') in ['staging', 'dev'] %}
{% do seomatic.script.get('googleTagManager').include(true) %}
{% endif %}
{# CookieBot.com script injection (and implement consentmode) #}
{{ craft.cookiebot.dialogScript()|replace('"auto"', '"auto" data-consentmode-defaults="disabled"')|raw }}
On a dev/staging environment, the GTM script from SEOMatic gets placed after the CookieBot code in the source HTML instead of before it, making it the in 3rd place instead of 2nd. (I know that on Production I would have to first disable the rendering of the script then grab it again)
Is it possible to make the {% do %} tag respect where it is placed in the source order of the template? Or is there another tag/function I can use so that I have full control of the order?
Thanks in advance for any help you might be able to give!
Ah i think this might be a duplicate of #1417
Your solution in #1417 was to tweak the GA script in the tracking scripts area, but because we do not have access to the wrapping <script>
tag in that config area I don't think this is going to work here.
I'm struggling to work out how to actually just render the GTM tag inbetween those 2 tags.
Ok, so i've got to this:
{% set gtmSEOMatic = seomatic.script.render('googleTagManager')|json_decode %}
{{ gtmSEOMatic.script|raw }}
With automatic rendering disabled. Ideally I wouldn't want to disable automatic rendering though, so open to your ideas/suggestions on this one.
The .script
renders both the head and the body script areas though, which adds an <iframe>
into the <head>
area, which is obviously not valid HTML :(
Maybe it would be good if you could add another node to the JSON of headScript
(as you already have the bodyScript node on that array)?
It would be great to be able to do something like (with automatic rendering turned on):
in the <head>
{# Disable automatic placement of GTM script #}
{% do seomatic.script.get('googleTagManager').include(false) %}
{# Grab SEOMatic GTM script object #}
{{ seomatic.script.render('googleTagManager')|json_decode }}
{# Output head portion of SEOMatic GTM script object #}
{{ gtmSEOMatic.headScript|raw }}
in the <body>
{# Output body portion of SEOMatic GTM script object #}
{{ gtmSEOMatic.bodyScript|raw }}
But currently, the render()
function will return nothing if I have set include()
to false
. (and ofc headScript
doesn't actually exist :))
Your solution in https://github.com/nystudio107/craft-seomatic/issues/1417 was to tweak the GA script in the tracking scripts area, but because we do not have access to the wrapping
You do, in that you can add whatever attributes you want to it:
The ability to do that was added specifically to allow you to work with things like CookieBot, CookieConsent, etc.
Hi Andrew, thanks for getting back to me, but what I meant by my comment about 'not having access to the <script>
tag' is that I can't insert the other 2 script tags that are required both above and below the GTM SEOmatic script tag.
In a nutshell, SEOMatic forces itself to insert the script tag into the source just before the </head>
tag. If I use the render function to control this, it requires me turning off auto-matic rendering and it will also then put an invalid <iframe>
tag into the <head>
area.
I need to do this:
-
<Consent Mode script tag>
-
<SEOMatic GTM script tag>
-
<CookieBot script tag>
plz obi wan welch, you're my only hope!
Well, I see two choices, if I'm understanding the issue properly:
-
You can just manually put the GTM script in yourself, as you are the Consent Mode and CookieBot scripts, and not use the SEOmatic tracking scripts
-
You can edit the script template for the GA script in SEOmatic -> Tracking Scripts to add in the Consent Mode script along with the GA script, so it's together, like this:
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments)
}
gtag("consent", "default", {
ad_personalization: "denied",
ad_storage: "denied",
ad_user_data: "denied",
analytics_storage: "denied",
functionality_storage: "denied",
personalization_storage: "denied",
security_storage: "granted",
wait_for_update: 500
});
gtag("set", "ads_data_redaction", true);
gtag("set", "url_passthrough", true);
{% if googleTagManagerId.value is defined and googleTagManagerId.value and not seomatic.helper.isPreview %}
{{ dataLayerVariableName.value }} = [{% if dataLayer is defined and dataLayer %}{{ dataLayer |json_encode() |raw }}{% endif %}];
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'{{ googleTagManagerUrl.value }}?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','{{ dataLayerVariableName.value }}','{{ googleTagManagerId.value }}');
{% endif %}
..and add the script attribute in:
And then include the CookieBot script in the <body>
tag or you could create your own tracking script in SEOmatic for the CookieBot script
For me this final method appears to work fine, however it looks to me like SEOmatic is rendered too far down the head.
In my Google Tag Assistant, it complains that the default consent state wasn't set in time.
If I hardcode the gtag consent + gtm tag directly in the head as the first thing, the error goes away.
For me this final method appears to work fine, however it looks to me like SEOmatic is rendered too far down the head.
In my Google Tag Assistant, it complains that the default consent state wasn't set in time.
If I hardcode the gtag consent + gtm tag directly in the head as the first thing, the error goes away.
Yes, I'm currently just hardcoding it all into the head which is a shame :(
I'm going to re-open this to see if there's something more interesting I can do about it
Thanks @khalwat , I really appreciate all your work and how many hours your plugins have saved me, so I hope you haven't thought I was moaning too much in this thread!
If you look at the https://support.cookiebot.com/hc/en-us/articles/360009192739-Google-Tag-Manager-and-Automatic-cookie-blocking page, and code example on that page, I guess my request would be "how could I replicate this identically with the GTM tracking scripts area in SEOMatic, with the proviso that <script>
tag 1 and 3 were manually added into the template by me"
Relevant here is also:
https://developers.google.com/tag-platform/security/guides/consent?consentmode=advanced
I think it's possible that if I bake this into the scripts in SEOmatic, this might get us most of the way there
My only thought is that maybe <script data-cookieconsent="ignore">
being on both the Consent Mode and GTM init tags is important? Or rather, that they are 2 separate script tags is important?
Otherwise, the main issue I had in a nutshell was that SEOmatic injected the script right before the closing </head>
tag so it was impossible to 'add' anything after it, which is what CookieBot required.
I think this is tangentially related to this issue. I noticed today that Google will no longer verify your site using the SEOmatic injected code format of placing the js link in the body.
The gtag install instructions now specify everything needs to be at the top of the head including the linked script and it seems like if it's not at least in the head then there can be issues getting analytics running.
Obviously there are other ways to verify or put the code in but ideally it would be supported in SEOmatic since I usually leave this stuff for the client to deal with.
@pixelmachine thanks I'll adjust the code to do that (but no, it's not related to this issue at all, because it's regarding gtag, not GTM, and it has nothing to do with the Cookie Consent issues described in this issue).
@robzor So my idea on this would be that there's an additional script you can enable, but I don't really want to get into adding every possible Cookie* implementation to SEOmatic.
So I'm thinking I might add a Before & After script field in SEOmatic for each Tracking Script, where you can put whatever you want in there, and have it load as you like.
This would also solve the unrelated problem with Google's change to their recommendations of how to implement gtag
as mentioned by @pixelmachine
Thanks @khalwat Yeah I mostly meant related in terms of code output from SEOmatic but yeah that is pretty tenuous.
@robzor So my idea on this would be that there's an additional script you can enable, but I don't really want to get into adding every possible Cookie* implementation to SEOmatic.
So I'm thinking I might add a Before & After script field in SEOmatic for each Tracking Script, where you can put whatever you want in there, and have it load as you like.
This would also solve the unrelated problem with Google's change to their recommendations of how to implement
gtag
as mentioned by @pixelmachine
Hi @khalwat , sorry I was away on holiday. The idea of a before and after field could work, sounds good to me :)