Slide-in survey

Thanks for testing out the Alpha version of the slide-in survey. This is the first version, so it might have some bugs. Be sure to report them so I can improve the script.

To get started, you’ll need publish access to GTM and admin access to GA4.

About the script

This tool was created to easily collect user feedback without having to buy another tool that slows down your website.

The idea came about because I, Sander Volbeda, missed the feedback feature from Hotjar while using Microsoft Clarity for heatmap and recordings tracking.

This is what it looks like:

image 7 - Sander Volbeda

Be aware, GA4 will only collect up to 100 characters. This includes spaces.

Script

Before you drop the code into your GTM container, it’s good to know something about the triggering.

This slide-in survey triggers when someone is on the page for 3 seconds. Once the survey is completed or closed, it won’t show again for 30 days. This information is stored in the local storage of the user’s browser.

<script>
(function() {
    'use strict';

    var lastClosed = localStorage.getItem('slideInLastClosed');
    var THIRTY_DAYS = 30 * 24 * 60 * 60 * 1000;
    var now = new Date().getTime();

    if (lastClosed && now - lastClosed < THIRTY_DAYS) {
        return;
    }

    var container = document.createElement('div');
    container.style.position = 'fixed';
    container.style.zIndex = '9999999999';
    container.style.right = '-320px';
    container.style.top = '50%';
    container.style.transform = 'translateY(-50%)';
    container.style.width = '100%';
    container.style.maxWidth = '320px';
    container.style.boxSizing = 'border-box';
    container.style.padding = '20px';
    container.style.backgroundColor = '#ffffff';
    container.style.boxShadow = '0 0 10px rgba(0,0,0,0.1)';
    container.style.borderRadius = '10px 0 0 10px';
    container.style.fontFamily = 'Arial, sans-serif';
    container.style.transition = 'right 0.3s ease-in-out';
    document.body.appendChild(container);

    var closeButton = document.createElement('button');
    closeButton.innerHTML = '&times;';
    closeButton.style.position = 'absolute';
    closeButton.style.right = '10px';
    closeButton.style.top = '10px';
    closeButton.style.background = 'none';
    closeButton.style.border = 'none';
    closeButton.style.fontSize = '24px';
    closeButton.style.cursor = 'pointer';
    closeButton.addEventListener('click', function() {
        container.style.right = '-320px';
        localStorage.setItem('slideInLastClosed', new Date().getTime());
        window.dataLayer.push({
            event: 'slide_in_closed',
            slide_in_closed: 'true'
        });
    });
    container.appendChild(closeButton);

    var question1 = document.createElement('p');
    question1.textContent = 'Do you enjoy using our product?';
    question1.style.fontWeight = 'bold';
    question1.style.marginBottom = '15px';
    container.appendChild(question1);

    var radioYes = createRadioButton('q1', 'yes', 'Yes');
    var radioNo = createRadioButton('q1', 'no', 'No');
    container.appendChild(radioYes);
    container.appendChild(radioNo);

    var question2 = document.createElement('div');
    question2.style.display = 'none';
    container.appendChild(question2);

    var submitButton = document.createElement('button');
    submitButton.textContent = 'Submit Feedback';
    submitButton.style.width = '100%';
    submitButton.style.display = 'none';
    submitButton.style.marginTop = '15px';
    submitButton.style.padding = '10px 20px';
    submitButton.style.backgroundColor = '#4CAF50';
    submitButton.style.color = 'white';
    submitButton.style.border = 'none';
    submitButton.style.borderRadius = '5px';
    submitButton.style.cursor = 'pointer';
    container.appendChild(submitButton);

    radioYes.querySelector('input').addEventListener('change', function() {
        handleQ1Answer('yes');
    });
    radioNo.querySelector('input').addEventListener('change', function() {
        handleQ1Answer('no');
    });

    submitButton.addEventListener('click', handleSubmit);

    function createRadioButton(name, value, label) {
        var wrapper = document.createElement('label');
        wrapper.style.display = 'block';
        wrapper.style.marginBottom = '10px';
        wrapper.style.cursor = 'pointer';

        var radio = document.createElement('input');
        radio.type = 'radio';
        radio.name = name;
        radio.value = value;
        radio.style.marginRight = '10px';

        var labelText = document.createTextNode(label);

        wrapper.appendChild(radio);
        wrapper.appendChild(labelText);

        return wrapper;
    }

    function handleQ1Answer(answer) {
        window.dataLayer.push({
            event: 'question_1_answered',
            question_1_answered: answer
        });

        question1.style.display = 'none';
        radioYes.style.display = 'none';
        radioNo.style.display = 'none';

        question2.innerHTML = '';
        question2.style.display = 'block';
        submitButton.style.display = 'block';

        var q2Text = document.createElement('p');
        q2Text.style.fontWeight = 'bold';
        q2Text.style.marginBottom = '15px';

        if (answer === 'yes') {
            q2Text.textContent = 'Great! What feature do you like the most?';
        } else {
            q2Text.textContent = 'We are sorry to hear that. How can we improve?';
        }
        question2.appendChild(q2Text);

        var input = document.createElement('textarea');
        input.id = 'q2-input';
        input.rows = 5;
        input.style.width = '100%';
        input.style.padding = '10px';
        input.style.borderRadius = '5px';
        input.style.border = '1px solid #ccc';
        input.maxLength = '100';
        question2.appendChild(input);

        var charCount = document.createElement('div');
        charCount.id = 'char-count';
        charCount.style.textAlign = 'right';
        charCount.style.fontSize = '12px';
        charCount.style.color = '#999';
        charCount.textContent = '0 / 100';
        question2.appendChild(charCount);

        input.addEventListener('input', function() {
            var currentLength = input.value.length;
            charCount.textContent = currentLength + ' / 100';
        });
    }

    function filterInput(input) {
        // Regular expression for email
        var emailPattern = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g;
        
        // Replace emails with [email]
        input = input.replace(emailPattern, '[email]');

        // Regular expression for phone numbers
        var phonePattern = /\b\d{8,}\b/g;
        
        // Replace phone numbers with [phone]
        input = input.replace(phonePattern, '[phone]');
        
        return input;
    }

    function handleSubmit() {
        var q1Answer = document.querySelector('input[name="q1"]:checked').value;
        var q2Answer = document.getElementById('q2-input').value;

        // Filter the q2 answer for emails and phone numbers
        q2Answer = filterInput(q2Answer);

        window.dataLayer.push({
            event: 'question_2_answered',
            question_2_answered: q2Answer
        });

        window.dataLayer.push({
            event: 'survey_completed',
            survey_completed: 'true'
        });

        showThankYouMessage();
    }

    function showThankYouMessage() {
        container.innerHTML = '';
        var thankYou = document.createElement('p');
        thankYou.textContent = 'Thank you for your feedback!';
        thankYou.style.fontWeight = 'bold';
        thankYou.style.textAlign = 'center';
        container.appendChild(thankYou);

        setTimeout(function() {
            container.style.right = '-320px';
            localStorage.setItem('slideInLastClosed', new Date().getTime());
        }, 3000);
    }

    setTimeout(function() {
        container.style.right = '0';
    }, 1000);
})();
</script>

Recent updates

10-07-’24:
1. Script update: Emails and phone numbers are filtered and will no longer show in GA4 due to privacy reasons. Since we can’t delete data from GA4 is very important that this personal data doesn’t end up in GA4.

08-07-’24:
1. Documentation: Added documentation on how to view survey results.

02-07-’24:
1. Tool: Started working on the builder (V1 soon will be ready).
2. Script update: Enhanced dataLayer data to events GTM documentation for GA4 analysis.
3. Tool: Creating Minified version of the script.

image - Sander Volbeda

01-07-’24:
1. Question 2: Added character count to second question.
2. Question 2: Changed ‘Submit’ button to ‘Submit Feedback’.
3. Question 2: Changed the width of the submit button to 100%.
4. Increased the size of the close element from 20px to 24px.

28-06-’24:
1. Added z-index to the SIS is always before the content of the website.

Data

The data that’s currently collected with this script:

Data collectedDescriptionEvent TriggeredDataLayer Variables
Question 1 answerUser’s response to “Do you enjoy using our product?”question_1_answeredevent: 'question_1_answered'<br>question_1_answered: 'yes' or 'no'
Question 2 answerUser’s response to the follow-up question based on their answer to Question 1question_2_answeredevent: 'question_2_answered'<br>question_2_answered: User's text input
Survey CompletedIndicates the survey has been completed after submitting the answer to Question 2survey_completedevent: 'survey_completed'<br>survey_completed: 'true'
Slide-In ClosedIndicates the slide-in was closed by the userslide_in_closedevent: 'slide_in_closed'<br>slide_in_closed: 'true'
Last Closed TimestampThe timestamp of when the slide-in was last closed, stored in localStorageNo event triggered directlyStored in localStorage as slideInLastClosed

GA4 setup

As mentioned before, you’ll need admin access to do this part. We’re going to set up Custom dimensions to access the data in the future.

image 1 - Sander Volbeda

Custom definitions

  1. Access GA4 Property
  2. Go to Google Analytics “Admin” > “Custom Definitions”
  3. Click on “Create custom dimension”
  4. Create the 4 custom dimension down below:

Dimension 1 – Question 1 answer

  • Dimension name: SIS Question 1
  • Scope: Event
  • Description: Slide-in survey: Stores the user’s answer to Question 1 (“yes” or “no”)
  • Event parameter: question_1_answered

Dimension 2 – Question 2 answer

  • Dimension name: SIS Question 2
  • Scope: Event
  • Description: Slide-in survey: Stores the user’s free-form text input in response to Question 2
  • Event parameter: question_2_answered

Dimension 3 – Survey completed

  • Dimension name: Survey completed
  • Scope: Event
  • Description: Slide-in survey: Indicates the survey has been completed
  • Event parameter: survey_completed

Dimension 4 – Slide-in closed

  • Dimension name: SIS Slide in closed
  • Scope: Event
  • Description: Slide-in survey: Indicates the slide-in was closed by the user
  • Event parameter: slide_in_closed

This data might take up to 48 hours to shown, I know … frustrating! That’s how GA4 works.

GTM (Google Tag Manager setup)

To do this, you’ll need publish access. It’s smart to do this in a new workspace in GTM and merge it once you’re done.

  1. Log in to Google Tag Manager (GTM)
  2. Go to your GTM container

Create Data Layer Variables for the events

  1. Click on “Variables” in the left-hand menu
  2. Click on the “New” button to create a new variable
image 2 - Sander Volbeda

For each variable:

  • Click on the “Variable Configuration” panel
  • Select “Data Layer Variable” as the Variable Type
  • Set the Data Layer Variable Name to match the corresponding data layer variable name
  • Name the variable (e.g., DLV - question_1_answered)
Variable nameData Layer Variable name
dlv – SIS – Question 1question_1_answered
dlv – SIS – Question 2question_2_answered
dlv – SIS – Survey completedsurvey_completed
dlv – SIS – Slide in closedslide_in_closed

Create triggers for the events

  1. Click on “Triggers” in the left-hand menu
  2. Click on the “New” button to create a new trigger
image 3 - Sander Volbeda

For each trigger:

  • Click on the “Trigger Configuration” panel
  • Select “Custom Event” as the Trigger Type
  • Set the Event Name to match the corresponding event name
  • Name the trigger (e.g., Trigger - Question 1 answered)
Trigger nameEvent name
Trigger – Question 1 answeredquestion_1_answered
Trigger – Question 2 answeredquestion_2_answered
Trigger – Survey completedsurvey_completed
Trigger – Slide-in closedslide_in_closed

Create tags for the events

  1. Click on “Tags” in the left-hand menu
  2. Click on the “New” button to create a new tag
image 4 - Sander Volbeda

For each tag:

  • Name the tag appropriately (e.g., “GA4 Event – SIS – Question 1 answered”)
  • Choose “Google Analytics: GA4 Event” as the Tag Type
  • Select your GA4 Configuration Tag
  • Set the Event Name to match the event in the dataLayer (e.g., question_1_answered)
  • Click on “Event Parameters” and add a custom parameter with the same name as the event
  • Set the value to the corresponding Data Layer variable (e.g., {{DL - question_1_answered}})
  • For each tag, attach the corresponding trigger: Click on “Triggering” and select the appropriate trigger (e.g., Trigger – Question 1 Answered for the “GA4 Event – Question 1 Answered” tag).
Tag nameEvent nameEvent parameter nameData layer variable
GA4 Event – SIS – Question 1 answeredquestion_1_answeredquestion_1_answereddlv – SIS – Question 1
GA4 Event – SIS – Question 2 answeredquestion_2_answeredquestion_2_answereddlv – SIS – Question 2
GA4 Event – SIS – Survey completedsurvey_completedsurvey_completeddlv – SIS – Survey completed
GA4 Event – SIS – Slide-In Closedslide_in_closedslide_in_closeddlv – SIS – Slide in closed

To analyse all the data correctly in GA4 you need to so some additional settings.

For the “GA4 Event – SIS – Question 2 answered,” you need to include the Event Parameters from “GA4 Event – SIS – Question 1 answered.” This allows you to filter the data in GA4 based on the answer to question one.

For the “GA4 Event – SIS – Survey completed,” you need to add the Event Parameters from both “GA4 Event – SIS – Question 1 answered” and “GA4 Event – SIS – Question 2 answered.”

Add the Script to the Website via GTM

image 5 - Sander Volbeda
  1. Click on “Tags” in the left-hand menu
  2. Click on the “New” button to create a new tag
  3. Name the tag (e.g., “Survey script”)
  4. Choose “Custom HTML” as the Tag Type
  5. Copy and paste the following script down below into the HTML text box
  6. Click on “Triggering” and choose the “All Pages” trigger or create a new trigger that specifies when and where the script should be executed
  7. Click on “Save” to save the tag configuration

Make sure to adjust the text to your wishes. You can search (CMD + F or CTRL + F) and replace these texts:

  • Do you enjoy using our product?
  • Great! What feature do you like the most?
  • We are sorry to hear that. How can we improve?
  • Thank you for your feedback!

Test if the events fire

  1. Click on the “Preview” button in the top right corner of GTM
  2. Enter the URL of the site where the survey is implemented and click “Connect.”
  3. On your site, interact with the survey to trigger each event (e.g., answer Question 1, answer Question 2, close the slide-in)
  4. In the GTM debug console, verify that each custom event (question_1_answered, question_2_answered, survey_completed, slide_in_closed) is firing as expected
  5. Check that the correct event parameters are being sent with each event
  6. Open GA4 and navigate to the DebugView
  7. Ensure that the events (question_1_answered, question_2_answered, survey_completed, slide_in_closed) appear in the DebugView
  8. Verify that the correct parameters are associated with each event

GTM firing events:

image 6 - Sander Volbeda

GA4 debugview:

image - Sander Volbeda

Publish container

  1. Click the “Submit” button in GTM.
  2. Enter a version name and description.
  3. Click “Publish” to make the changes live.

Google Analytics 4 analysis

I’ll be recording videos of this process in the future, but here’s a quick summary:

The easiest way to view the survey results:

  1. Open Google Analytics.
  2. Open Exploration reports.
  3. Create a new report.
  4. Add the following dimensions:
    • SIS Question 1
    • SIS Question 2
    • SIS Survey completed
    • SIS Slide in closed
  5. Place these dimensions in the ROWS area.
  6. Add the metric(s) (e.g., Total users).
  7. Place the metric(s) in the VALUES area.

Optional:

  • Add a filter:
    1. Click on “Add filter.”
    2. Select a dimension or metric (e.g., SIS Question 1).
    3. Set the conditions:
      • Match type: Exactly matches
      • Expression: yes

This filter will show all responses where the first question equals “yes.” You can also reverse this to show where the answers equal “no.”

Privacy (GDPR)

Although we don’t collect personal data with the slide-in, GA4 does anyway. Since this tool works by sending data to GA4, it has to be behind the cookie bar. Make sure the slide-in only works if GA4 data collection is accepted!

I don’t collect any data! It all works with your GTM/GA4 account without any interference.

Final thoughts

For sure I’ve got to find a way to optimize the GTM process without having so many triggers/tags. I hope you can now collect some additional user feedback!

Doing:

  1. Create script builder: Make it possible to style the slide-in, adjust text, and adjust settings without having to code.
  2. Minify the script for faster loading times.

Next steps:

  1. Optimize GTM process if possible (less triggers, tags, etc.).
  2. Enhance documentation for page targeting.
  3. Enhance documentation for running multiple slide-ins at the same time.
  4. Enhance documentation to sent more data to GA4 (like: page type)
  5. Add ‘rating’ option for feedback, enhanced with a second question.

Future:

  • Easily sent data to BigQuery
  • Access data Looker Studio tutorial

Logo Freelance CRO specialist Sander Volbeda

Working remotely from Groningen, the Netherlands. Get in touch and let’s schedule a meeting, no strings attached.

Get in touch
© 2024 Sander Volbeda, All rights reserved
CoC: 53236734 VAT: NL002247968B39