Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Positioning of GTM code #1426

Open
robzor opened this issue Feb 29, 2024 · 17 comments
Open

Positioning of GTM code #1426

robzor opened this issue Feb 29, 2024 · 17 comments
Labels

Comments

@robzor
Copy link

robzor commented Feb 29, 2024

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!

@robzor
Copy link
Author

robzor commented Feb 29, 2024

Ah i think this might be a duplicate of #1417

@robzor
Copy link
Author

robzor commented Feb 29, 2024

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.

@robzor
Copy link
Author

robzor commented Feb 29, 2024

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 :))

@khalwat
Copy link
Collaborator

khalwat commented Mar 1, 2024

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.

You do, in that you can add whatever attributes you want to it:
Screenshot 2024-02-29 at 10 51 31 PM

The ability to do that was added specifically to allow you to work with things like CookieBot, CookieConsent, etc.

@khalwat khalwat closed this as completed Mar 1, 2024
@robzor
Copy link
Author

robzor commented Mar 3, 2024

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:

  1. <Consent Mode script tag>
  2. <SEOMatic GTM script tag>
  3. <CookieBot script tag>

plz obi wan welch, you're my only hope!

@khalwat
Copy link
Collaborator

khalwat commented Mar 4, 2024

Well, I see two choices, if I'm understanding the issue properly:

  1. 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

  2. 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:

Screenshot 2024-02-29 at 10 51 31 PM

And then include the CookieBot script in the <body> tag or you could create your own tracking script in SEOmatic for the CookieBot script

@mijewe
Copy link

mijewe commented Mar 14, 2024

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.

@robzor
Copy link
Author

robzor commented Mar 14, 2024

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 :(

@khalwat
Copy link
Collaborator

khalwat commented Mar 15, 2024

I'm going to re-open this to see if there's something more interesting I can do about it

@khalwat khalwat reopened this Mar 15, 2024
@robzor
Copy link
Author

robzor commented Mar 15, 2024

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"

@khalwat
Copy link
Collaborator

khalwat commented Mar 15, 2024

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

@robzor
Copy link
Author

robzor commented Mar 19, 2024

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.

@pixelmachine
Copy link

pixelmachine commented Aug 9, 2024

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.

Screenshot 2024-08-08 at 7 46 29 PM

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.

@khalwat
Copy link
Collaborator

khalwat commented Aug 9, 2024

@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).

@khalwat
Copy link
Collaborator

khalwat commented Aug 10, 2024

@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

@pixelmachine
Copy link

Thanks @khalwat
Yeah I mostly meant related in terms of code output from SEOmatic but yeah that is pretty tenuous.

@robzor
Copy link
Author

robzor commented Aug 15, 2024

@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 :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants