-
-
Notifications
You must be signed in to change notification settings - Fork 36
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
Snippet to track load abandonments (bounces) #1355
Comments
Here's what I ended up with. Testing on Nebula Documentation site only: add_action('nebula_head_open', 'ga_track_load_abandons');
function ga_track_load_abandons(){
?>
<script>
window.__trackAbandons = () => {
document.removeEventListener('visibilitychange', window.__trackAbandons); //Remove the listener so it only runs once.
//Send the data to Google Analytics via the Measurement Protocol.
navigator.sendBeacon && navigator.sendBeacon('https://www.google-analytics.com/collect', [
'v=1',
't=event',
'ec=Load',
'ea=abandon',
'ni=1',
'tid=<?php echo nebula()->get_option('ga_tracking_id'); ?>',
'cid=' + document.cookie.replace(/(?:(?:^|.*;)\s*_ga\s*\=\s*(?:\w+\.\d\.)([^;]*).*$)|^.*$/, '$1') || (Math.random() * Math.pow(2, 52)),
'ev=' + Math.round(performance.now()),
].join('&'));
};
document.addEventListener('visibilitychange', window.__trackAbandons);
//Remove the load abandon tracker on window load
window.onload = function(){
document.removeEventListener('visibilitychange', window.__trackAbandons);
};
</script>
<?php
} I have some questions/concerns before shipping this to production about how it affects other reports since we're sending an event before/without a pageview. |
Just noting that this event does not have contextual information that comes with a pageview. I've updated the payload to include more info, but it will never be as effective as a true pageview. I'm sure this will affect other reports. I've also sent a second payload as a User Timing. This is a sampled report, so it won't show a true number of load abandons, but could provide some more information. |
Updated again. This code now sends the event with a label if GA is ready or not.
Added a raw JS event trigger in analytics.php that we can listen to for when GA is ready (to supplement the window var). Not using it (see commented out above), but could consider. |
Might be cool to try all bounces regardless of https://developers.google.com/analytics/devguides/collection/analyticsjs/tasks Here's a snippet to start:
Not sure what data is available in individual payloads- like, if we can even tell if the user is a bounce. Interactive vs. Non-Interactive events would be easy, but I don't want to get crazy complicated needing to set my own cookie for multiple pageviews. |
Updated again to include window.__trackAbandons = (e) => {
document.removeEventListener('visibilitychange', window.__trackAbandons); //Remove the listeners so it only runs once.
document.removeEventListener('beforeunload', window.__trackAbandons); //Remove the listeners so it only runs once.
//Event
//This could mess with other reports as an event could be sent before/without a pageview
loadAbandonLabel = 'Before GA was ready';
if ( window.GAready ){
loadAbandonLabel = 'After GA was ready';
}
//https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters
navigator.sendBeacon && navigator.sendBeacon('https://www.google-analytics.com/collect', [
'tid=<?php echo nebula()->get_option('ga_tracking_id'); ?>', //Tracking ID
'cid=' + document.cookie.replace(/(?:(?:^|.*;)\s*_ga\s*\=\s*(?:\w+\.\d\.)([^;]*).*$)|^.*$/, '$1') || (Math.random() * Math.pow(2, 52)), //Client ID
'v=1', //Protocol Version
't=event', //Hit Type
'ec=Load', //Event Category
'ea=Abandon+(' + e.type + ')', //Event Action
'el=' + loadAbandonLabel + ' (and before window load)', //Event Label
'ev=' + Math.round(performance.now()), //Event Value
'ni=1', //Non-Interaction Hit
'dr=<?php echo ( isset($_SERVER['HTTP_REFERER']) )? $_SERVER['HTTP_REFERER'] : ''; ?>', //Document Referrer
'dl=' + window.location.href, //Document Location URL
'dt=' + document.title, //Document Title
].join('&'));
//User Timing
//These are sampled, so it might not provide an accurate number of abandons
navigator.sendBeacon && navigator.sendBeacon('https://www.google-analytics.com/collect', [
'tid=<?php echo nebula()->get_option('ga_tracking_id'); ?>', //Tracking ID
'cid=' + document.cookie.replace(/(?:(?:^|.*;)\s*_ga\s*\=\s*(?:\w+\.\d\.)([^;]*).*$)|^.*$/, '$1') || (Math.random() * Math.pow(2, 52)), //Client ID
'v=1', //Protocol Version
't=timing', //Hit Type
'utc=Load Abandon', //Timing Category
'utv=Abandon', //Timing Variable Name
'utt=' + Math.round(performance.now()), //Timing Time (milliseconds)
'utl=' + loadAbandonLabel + ' (and before window load)', //Timing Label
'dl=' + window.location.href, //Document Location URL
'dt=' + document.title, //Document Title
].join('&'));
};
document.addEventListener('visibilitychange', window.__trackAbandons);
document.addEventListener('beforeunload', window.__trackAbandons);
//Remove the load abandon tracker on window load
window.onload = function(){
document.removeEventListener('visibilitychange', window.__trackAbandons);
document.removeEventListener('beforeunload', window.__trackAbandons);
}; |
Still sort of feeling around on this, but I've updated it again to include "Hard" (unload) or "Soft" (changing tab/window) abandonments. window.__trackAbandons = (e) => {
//Remove the listeners so it only runs once.
document.removeEventListener('visibilitychange', window.__trackAbandons);
document.removeEventListener('beforeunload', window.__trackAbandons);
document.removeEventListener('unload', window.__trackAbandons);
//Event
//This could mess with other reports as an event could be sent before/without a pageview
loadAbandonLevel = 'Hard (Unload)';
if ( e.type == 'visibilitychange' ){
loadAbandonLevel = 'Soft (Visibility Change)';
}
loadAbandonLabel = 'Before GA was ready';
if ( window.GAready ){
loadAbandonLabel = 'After GA was ready';
}
//https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters
navigator.sendBeacon && navigator.sendBeacon('https://www.google-analytics.com/collect', [
'tid=<?php echo nebula()->get_option('ga_tracking_id'); ?>', //Tracking ID
'cid=' + document.cookie.replace(/(?:(?:^|.*;)\s*_ga\s*\=\s*(?:\w+\.\d\.)([^;]*).*$)|^.*$/, '$1') || (Math.random() * Math.pow(2, 52)), //Client ID
'v=1', //Protocol Version
't=event', //Hit Type
'ec=Load Abandon', //Event Category
'ea=' + loadAbandonLevel, //Event Action
'el=' + loadAbandonLabel + ' (and before window load)', //Event Label
'ev=' + Math.round(performance.now()), //Event Value
'ni=1', //Non-Interaction Hit
'dr=<?php echo ( isset($_SERVER['HTTP_REFERER']) )? $_SERVER['HTTP_REFERER'] : ''; ?>', //Document Referrer
'dl=' + window.location.href, //Document Location URL
'dt=' + document.title, //Document Title
].join('&'));
//User Timing
//These are sampled, so it might not provide an accurate number of abandons
navigator.sendBeacon && navigator.sendBeacon('https://www.google-analytics.com/collect', [
'tid=<?php echo nebula()->get_option('ga_tracking_id'); ?>', //Tracking ID
'cid=' + document.cookie.replace(/(?:(?:^|.*;)\s*_ga\s*\=\s*(?:\w+\.\d\.)([^;]*).*$)|^.*$/, '$1') || (Math.random() * Math.pow(2, 52)), //Client ID
'v=1', //Protocol Version
't=timing', //Hit Type
'utc=Load Abandon', //Timing Category
'utv=' + loadAbandonLevel, //Timing Variable Name
'utt=' + Math.round(performance.now()), //Timing Time (milliseconds)
'utl=' + loadAbandonLabel + ' (and before window load)', //Timing Label
'dl=' + window.location.href, //Document Location URL
'dt=' + document.title, //Document Title
].join('&'));
};
document.addEventListener('visibilitychange', window.__trackAbandons);
document.addEventListener('beforeunload', window.__trackAbandons);
document.addEventListener('unload', window.__trackAbandons);
//Remove the load abandon tracker on window load
window.onload = function(){
document.removeEventListener('visibilitychange', window.__trackAbandons);
document.removeEventListener('beforeunload', window.__trackAbandons);
document.removeEventListener('unload', window.__trackAbandons);
}; I don't know if the |
Note: This will live in the |
Here's the final script. Reworked to be easier to read and for some better performance (and removed some unnecessary bits). I placed this in the class document.addEventListener("visibilitychange", newAbandonTracker);
window.onbeforeunload = newAbandonTracker;
function newAbandonTracker(e){
if ( e.type == 'visibilitychange' && document.visibilityState == 'visible' ){
return false;
}
//Remove listeners so this can only trigger once
document.removeEventListener("visibilitychange", newAbandonTracker);
window.onbeforeunload = null;
loadAbandonLevel = 'Hard (Unload)';
if ( e.type == 'visibilitychange' ){
loadAbandonLevel = 'Soft (Visibility Change)';
}
navigator.sendBeacon && navigator.sendBeacon('https://www.google-analytics.com/collect', [
'tid=<?php echo nebula()->get_option('ga_tracking_id'); ?>', //Tracking ID
'cid=' + document.cookie.replace(/(?:(?:^|.*;)\s*_ga\s*\=\s*(?:\w+\.\d\.)([^;]*).*$)|^.*$/, '$1') || (Math.random()*Math.pow(2, 52)), //Client ID
'v=1', //Protocol Version
't=event', //Hit Type
'ec=Load Abandon', //Event Category
'ea=' + loadAbandonLevel, //Event Action
'el=Before window load', //Event Label
'ev=' + Math.round(performance.now()), //Event Value
'ni=1', //Non-Interaction Hit
'dr=<?php echo ( isset($_SERVER['HTTP_REFERER']) )? $_SERVER['HTTP_REFERER'] : ''; ?>', //Document Referrer
'dl=' + window.location.href, //Document Location URL
'dt=' + document.title, //Document Title
].join('&'));
//User Timing
navigator.sendBeacon && navigator.sendBeacon('https://www.google-analytics.com/collect', [
'tid=<?php echo nebula()->get_option('ga_tracking_id'); ?>', //Tracking ID
'cid=' + document.cookie.replace(/(?:(?:^|.*;)\s*_ga\s*\=\s*(?:\w+\.\d\.)([^;]*).*$)|^.*$/, '$1') || (Math.random()*Math.pow(2, 52)), //Client ID
'v=1', //Protocol Version
't=timing', //Hit Type
'utc=Load Abandon', //Timing Category
'utv=' + loadAbandonLevel, //Timing Variable Name
'utt=' + Math.round(performance.now()), //Timing Time (milliseconds)
'utl=Before window load', //Timing Label
'dl=' + window.location.href, //Document Location URL
'dt=' + document.title, //Document Title
].join('&'));
}
//Remove abandonment listeners on window load
window.addEventListener('load', function(){
document.removeEventListener('visibilitychange', loadAbandonTracking);
if ( window.onbeforeunload === loadAbandonTracking ){
window.onbeforeunload = null;
}
}); Events vs. Timings:I decided to keep both the event and the timing. Eventually when I'm convinced one way or another I can remove one. Some thoughts on these:
|
Note: This does not work in Safari or any IE (it does work in Edge), so while the total number of load abandons will not be 100% accurate, the timings will be. Eventually the total abandons accuracy will increase as browser support grows and usage of old browsers dies. |
RetrofittingTo retrofit into old procedural sites, use the following PHP snippet. I put this in (Note: This has been edited to include the visibility check from further below) //Load abandonment tracking
add_action('nebula_head_open', 'ga_track_load_abandons'); //This is the earliest anything can be placed in the <head>
function ga_track_load_abandons(){
if ( nebula_is_bot() ){ //This may not exist on really old sites
return false;
}
?>
<script>
document.addEventListener('visibilitychange', loadAbandonTracking);
window.onbeforeunload = loadAbandonTracking;
function loadAbandonTracking(e){
if ( e.type == 'visibilitychange' && document.visibilityState == 'visible' ){
return false;
}
//Remove listeners so this can only trigger once
document.removeEventListener('visibilitychange', loadAbandonTracking);
window.onbeforeunload = null;
loadAbandonLevel = 'Hard (Unload)';
if ( e.type == 'visibilitychange' ){
loadAbandonLevel = 'Soft (Visibility Change)';
}
//Grab the Google Analytics CID from the cookie (if it exists)
gaCID = document.cookie.replace(/(?:(?:^|.*;)\s*_ga\s*\=\s*(?:\w+\.\d\.)([^;]*).*$)|^.*$/, '$1');
newReturning = 'Returning visitor or multiple pageview session';
if ( !gaCID ){
gaCID = (Math.random()*Math.pow(2, 52));
newReturning = 'New user or blocking Google Analytics cookie';
}
navigator.sendBeacon && navigator.sendBeacon('https://www.google-analytics.com/collect', [
'tid=<?php echo nebula_option('ga_tracking_id'); ?>', //Tracking ID
'cid=' + gaCID, //Client ID
'v=1', //Protocol Version
't=event', //Hit Type
'ec=Load Abandon', //Event Category
'ea=' + loadAbandonLevel, //Event Action
'el=' + newReturning, //Event Label
'ev=' + Math.round(performance.now()), //Event Value
'ni=1', //Non-Interaction Hit
'dr=<?php echo ( isset($_SERVER['HTTP_REFERER']) )? $_SERVER['HTTP_REFERER'] : ''; ?>', //Document Referrer
'dl=' + window.location.href, //Document Location URL
'dt=' + document.title, //Document Title
].join('&'));
//User Timing
navigator.sendBeacon && navigator.sendBeacon('https://www.google-analytics.com/collect', [
'tid=<?php echo nebula_option('ga_tracking_id'); ?>', //Tracking ID
'cid=' + gaCID, //Client ID
'v=1', //Protocol Version
't=timing', //Hit Type
'utc=Load Abandon', //Timing Category
'utv=' + loadAbandonLevel, //Timing Variable Name
'utt=' + Math.round(performance.now()), //Timing Time (milliseconds)
'utl=' + newReturning, //Timing Label
'dl=' + window.location.href, //Document Location URL
'dt=' + document.title, //Document Title
].join('&'));
}
//Remove abandonment listeners on window load
window.addEventListener('load', function(){
document.removeEventListener('visibilitychange', loadAbandonTracking);
if ( window.onbeforeunload === loadAbandonTracking ){
window.onbeforeunload = null;
}
});
</script>
<?php
} |
Another thing I was just thinking about is if the user opens it in a new tab and then changes to that tab while it is still loading, it will trigger a false-positive abandonment. I should look into checking what type of visibility change happened to make sure it is changing from visible to hidden before sending the event. |
Ok, added this to the top of the function: if ( e.type == 'visibilitychange' && document.visibilityState == 'visible' ){
return false;
} I also added a return false to prevent bots from being tracked (Note: this is PHP not JS). if ( $this->is_bot() ){
return false;
} |
That discrepancy was due to a race condition on window load. jQuery was overriding the previous function, so the abandonment event listeners were never removed. I've updated Nebula and will update the retrofit function above, too. |
I added a check for new vs. returning visitor (or multipage session). Not going to update any of the previous snippets except for the retrofit one now because I'm sure I'll be making little tweaks like this constantly. |
As a note- with the new Hit ID dimension we're able to get median data in Google Analytics. |
This is from Google I/O 2017 that I watched, but didn't catch the abandonment tracking part...
https://developers.google.com/web/updates/2017/06/user-centric-performance-metrics
This snippet tracks load abandonments in GA. Research and test it quite a bit more before we implement it.
Remove the listener once the page becomes interactive:
document.removeEventListener('visibilitychange', window.__trackAbandons);
Make sure that this does not conflict with Autotrack's pagevisibility tracking or if it is already implemented within that library.
The text was updated successfully, but these errors were encountered: