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

[Bug]: Uncaught (in promise) TypeError: Cannot read properties of null (reading 'appendChild') #676

Open
mrleblanc101 opened this issue Apr 23, 2024 · 7 comments
Labels
bug Something isn't working

Comments

@mrleblanc101
Copy link

mrleblanc101 commented Apr 23, 2024

Expected Behavior

I'm trying to implement CookieConsent with GTM Consent Mode.
Here is my config:

<script>
    window.dataLayer = window.dataLayer || [];
    function gtag() { window.dataLayer.push(arguments); }

    gtag('consent', 'default', {
        ad_storage: 'denied',
        ad_user_data: 'denied',
        ad_personalization: 'denied',
        analytics_storage: 'denied',
        functionality_storage: 'denied',
        personalization_storage: 'denied',
        security_storage: 'denied',
    });

    gtag('consent', 'update', {
        functionality_storage: CookieConsent.acceptedCategory('functionality') ? 'granted' : 'denied',
        ad_storage: CookieConsent.acceptedCategory('marketing') ? 'granted' : 'denied',
        ad_user_data: CookieConsent.acceptedCategory('marketing') ? 'granted' : 'denied',
        ad_personalization: CookieConsent.acceptedCategory('marketing') ? 'granted' : 'denied',
        analytics_storage: CookieConsent.acceptedCategory('analytics') ? 'granted' : 'denied',
        personalization_storage: CookieConsent.acceptedCategory('personalization') ? 'granted' : 'denied',
        security_storage: CookieConsent.acceptedCategory('security') ? 'granted' : 'denied',
    });
    CookieConsent.run({
        guiOptions: {
            consentModal: {
                layout: 'box wide',
                position: 'bottom right',
                equalWeightButtons: true,
                flipButtons: true,
            },
            preferencesModal: {
                layout: 'box',
                position: 'right',
                equalWeightButtons: true,
                flipButtons: false,
            },
        },
        categories: {
            necessary: {
                readOnly: true,
            },
            marketing: {},
            analytics: {},
            functionality: {},
            personalization: {},
            security: {},
        },
        language: { ... }
        onConsent: (...args) => {
            manageConsent(args);
            window.dataLayer.push({ event: 'consent_ready' });
        },
        onChange: (...args) => {
            manageConsent(args);
            window.dataLayer.push({ event: 'consent_update' });
        },
    });

    function manageConsent() {
        gtag('consent', 'update', {
            functionality_storage: CookieConsent.acceptedCategory('functionality') ? 'granted' : 'denied',
            ad_storage: CookieConsent.acceptedCategory('marketing') ? 'granted' : 'denied',
            ad_user_data: CookieConsent.acceptedCategory('marketing') ? 'granted' : 'denied',
            ad_personalization: CookieConsent.acceptedCategory('marketing') ? 'granted' : 'denied',
            analytics_storage: CookieConsent.acceptedCategory('analytics') ? 'granted' : 'denied',
            personalization_storage: CookieConsent.acceptedCategory('personalization') ? 'granted' : 'denied',
            security_storage: CookieConsent.acceptedCategory('security') ? 'granted' : 'denied',
        });
    }
</script>
<script>(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= 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); })(window,document,'script','dataLayer','GTM-XXXXXXXX');</script>

Current Behavior

I understand that the errors is because the plugin try to insert the banner into the DOM, while the page has not finished loading (DOMContentLoaded).

My issue is that if I put CookieConsent.run(...) inside a window.addEventListener('load', () => { ... }), the Container Loaded event will be fired before GTM receive the consent value from gtag() inside onConsent, which mean Facebook Pixel is blocked even if the user clicked Accept All.

Screenshot 2024-04-23 at 9 37 48 AM

Screenshot 2024-04-23 at 9 37 57 AM

Steps to reproduce

.

Proposed fix or additional info.

Would it be possible to execute the "cc.run()" command as soon as possible, and only delay the insertion of the banner into the DOM once the page is loaded ? Currently, I need to wrap the whole cc.run() inside the window.addEventListener('load', () => { ... }) which delay the plugin initialization which does not require access to DOM and could be executed earlier.

Version

3.0.1

On which browser do you see the issue?

Chrome

@mrleblanc101 mrleblanc101 added the bug Something isn't working label Apr 23, 2024
@github-actions github-actions bot added the triage yet to be reviewed label Apr 23, 2024
@mrleblanc101
Copy link
Author

Also, if I run CookieConsent.acceptedCategory('functionality') ? 'granted' : 'denied' before CookieConsent.run(...), it will always return denied even if the cookie is set !

@mrleblanc101
Copy link
Author

Should I move all my tags from All pages trigger to DOM Ready or Window Loaded instead ?
This seems odd as All pages is the default for GA4 when using to Google Template.

@orestbida
Copy link
Owner

Would it be possible to execute the "cc.run()" command as soon as possible

By default the plugin is loaded asynchronously, but you can change this behaviour via the lazyHtmlGeneration option.

Also, if I run CookieConsent.acceptedCategory('functionality') ? 'granted' : 'denied' before CookieConsent.run(...), it will always return denied even if the cookie is set !

Yes, that's the expected behaviour; the only method that can be run before the plugin's own initialization is getCookie. You can access the acceptedCategories before the plugin's initialization like this:

const acceptedCategories = CookieConsent.getCookie('categories', 'cc_cookie') || [];
// acceptedCategories.includes('analytics')

CookieConsent.run({...});

@orestbida orestbida removed the triage yet to be reviewed label Apr 24, 2024
@mrleblanc101
Copy link
Author

mrleblanc101 commented Apr 24, 2024

@orestbida

By default the plugin is loaded asynchronously, but you can change this behaviour via the lazyHtmlGeneration option.

Maybe I'm not understanding correctly, lazyHtmlGeneration seems to default to true in the documentation, yet it I still have the error. I also tried explicitely:

CookieConsent.run({
    autoShow: false,
    lazyHtmlGeneration: true,
    ...
});

Yes, that's the expected behaviour; the only method that can be run before the plugin's own initialization is getCookie. You can access the acceptedCategories before the plugin's initialization like this:

Thanks, I guess that will do, but I think it would be nice to decouple initialization and injection into the DOM.
I thought about reading the cookie manually using a cookie library like js-cookie, but I thought it was overkill.
Nice to know I can use CookieConsent.getCookie before init.

@mrleblanc101
Copy link
Author

mrleblanc101 commented Apr 24, 2024

Let me know If you want me to close this or you'd like to keep it open for doc/exemple update.
Here is my final results if it can help someone else:

<script src="https://cdn.jsdelivr.net/gh/orestbida/cookieconsent@3.0.1/dist/cookieconsent.umd.js"></script>
<script>
    window.dataLayer = window.dataLayer || [];
    function gtag() { window.dataLayer.push(arguments); }

    gtag('consent', 'default', {
        ad_storage: 'denied',
        ad_user_data: 'denied',
        ad_personalization: 'denied',
        analytics_storage: 'denied',
        functionality_storage: 'denied',
        personalization_storage: 'denied',
        security_storage: 'denied',
    });

    if(CookieConsent.getCookie('categories', 'cc_cookie')) {
        const acceptedCategories = CookieConsent.getCookie('categories', 'cc_cookie') || [];
        gtag('consent', 'update', {
            functionality_storage: acceptedCategories.includes('functionality') ? 'granted' : 'denied',
            ad_storage: acceptedCategories.includes('marketing') ? 'granted' : 'denied',
            ad_user_data: acceptedCategories.includes('marketing') ? 'granted' : 'denied',
            ad_personalization: acceptedCategories.includes('marketing') ? 'granted' : 'denied',
            analytics_storage: acceptedCategories.includes('analytics') ? 'granted' : 'denied',
            personalization_storage: acceptedCategories.includes('personalization') ? 'granted' : 'denied',
            security_storage: acceptedCategories.includes('security') ? 'granted' : 'denied',
        });
    }

    window.addEventListener('load', function () {
        CookieConsent.run({
            guiOptions: {
                consentModal: {
                    layout: 'box wide',
                    position: 'bottom right',
                    equalWeightButtons: true,
                    flipButtons: true,
                },
                preferencesModal: {
                    layout: 'box',
                    position: 'right',
                    equalWeightButtons: true,
                    flipButtons: false,
                },
            },
            categories: {
                necessary: {
                    readOnly: true,
                },
                marketing: {},
                analytics: {},
                functionality: {},
                personalization: {},
                security: {},
            },
            language: { ... },
            onFirstConsent: (...args) => {
                manageConsent(args);
                window.dataLayer.push({ event: 'consent_ready' });
            },
            onChange: (...args) => {
                manageConsent(args);
                window.dataLayer.push({ event: 'consent_update' });
            },
        });
    });

    function manageConsent() {
        gtag('consent', 'update', {
            functionality_storage: CookieConsent.acceptedCategory('functionality') ? 'granted' : 'denied',
            ad_storage: CookieConsent.acceptedCategory('marketing') ? 'granted' : 'denied',
            ad_user_data: CookieConsent.acceptedCategory('marketing') ? 'granted' : 'denied',
            ad_personalization: CookieConsent.acceptedCategory('marketing') ? 'granted' : 'denied',
            analytics_storage: CookieConsent.acceptedCategory('analytics') ? 'granted' : 'denied',
            personalization_storage: CookieConsent.acceptedCategory('personalization') ? 'granted' : 'denied',
            security_storage: CookieConsent.acceptedCategory('security') ? 'granted' : 'denied',
        });
    }
</script>
<script>(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= 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); })(window,document,'script','dataLayer','GTM-XXXXXXXX');</script>

@orestbida
Copy link
Owner

Perhaps I wasn't clear, you should set lazyHtmlGeneration to false to run the plugin synchronously.

We can leave this open, until there is a docs. section on how to set up GTM.

@mrleblanc101
Copy link
Author

mrleblanc101 commented Apr 24, 2024

@orestbida I tried false too and i had the exact same issue, but anyway I posted my solution above.

Thanks again for your help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants