Capturing page titles accurately for timeonsite tracker in a single-page Angular app is challenging when navigating to other pages

Implemented the timeonsite JS tracker in my Angular web application using HTML tags as shown below,

<script type="text/javascript">
var Tos;
(function(d, s, id, file) {
    var js, fjs = d.getElementsByTagName(s)[0];
    if (d.getElementById(id)) return;
    js = d.createElement(s);
    js.id = id;
    js.onload = function() {
        var config = {
            trackBy: 'seconds',

            blacklistUrl: ['http://nature-home.local/#contact'],
            
            developerMode: true, //optional setting
            
            callback: function(data) {
                console.log(data);
                // provide your endpoint URL/ server-side URL for handling TOS data via a POST method. Example PHP, nodejs or python URL to save this data to your DB
                var endPointUrl = 'http://nature-home.local/api/tos'; // replace with your endpoint URL
                if (data && data.trackingType) {
                    if (data.trackingType == 'tos') {
                        if (Tos.verifyData(data) != 'valid') {
                            console.log('Data discarded!');
                            return; 
                        }
                    }
                    
                    // utilize the sendBeacon API in your browser.
                    if (navigator && typeof navigator.sendBeacon === 'function') {
                        var blob = new Blob([JSON.stringify(data)], {type : 'application/json'});
                        navigator.sendBeacon(endPointUrl, blob);
                    }
                }
            }
            
        };
        if(TimeOnSiteTracker) {
            Tos = new TimeOnSiteTracker(config);
        }
    };
    js.src = file;fjs.parentNode.insertBefore(js, fjs);
} (document, 'script', 'TimeOnSiteTracker', 'js/timeonsitetracker.js'));

Upon page reloads and refreshes, I can see the captured data stored in the tracking table accurately. I am leveraging the sendBeacon() API to capture data efficiently and without loss.

From the page, the recorded information appears as follows:

{
    "TOSId": 10271953114371370,
    "TOSSessionKey": "6454165596535779179057",
    "TOSUserId": "anonymous",
    "URL": "http://nature-home.local/#home",
    "title": "Nature Posts - Home",
    "entryTime": "2022-06-23 06:22:37.787",
    "currentTime": "2022-06-23 06:22:49.489",
    "timeOnPage": 12,
    "timeOnPageTrackedBy": "second",
    "timeOnSite": 12,
    "timeOnPageByDuration": "0d 00h 00m 12s",
    "timeOnSiteByDuration": "0d 00h 00m 12s",
    "trackingType": "tos"
}

For the page, the captured record is displayed as:

{
    "TOSId": 15426974499706884,
    "TOSSessionKey": "6454165596535779179057",
    "TOSUserId": "anonymous",
    "URL": "http://nature-home.local/#home",
    "title": "Nature Posts - Home",
    "entryTime": "2022-06-23 06:24:49.449",
    "currentTime": "2022-06-23 06:24:52.497",
    "timeOnPage": 3,
    "timeOnPageTrackedBy": "second",
    "timeOnSite": 15,
    "timeOnPageByDuration": "0d 00h 00m 03s",
    "timeOnSiteByDuration": "0d 00h 00m 15s",
    "trackingType": "tos"
}

And from the page, the captured data displays as:

  {
    "TOSId": 4699630142561574,
    "TOSSessionKey": "6454165596535779179057",
    "TOSUserId": "anonymous",
    "URL": "http://nature-home.local/#home",
    "title": "Nature Posts - Home",
    "entryTime": "2022-06-23 06:25:18.873",
    "currentTime": "2022-06-23 06:25:29.624",
    "timeOnPage": 11,
    "timeOnPageTrackedBy": "second",
    "timeOnSite": 26,
    "timeOnPageByDuration": "0d 00h 00m 11s",
    "timeOnSiteByDuration": "0d 00h 00m 26s",
    "trackingType": "tos"
  }

It's worth noting that the URL field points only to the homepage across all three page navigations instead of the specific /products and /photos pages respectively.

Could this be a bug within the timeonsitetracker.js library or is there something crucial I might be overlooking? I have not encountered this issue in regular web applications (non single-page apps). Appreciate any insights provided in advance.

Answer №1

Could there be a glitch in timeonsitetracker.js library or is there something I might be overlooking? Just to clarify, this issue hasn't surfaced in the non single-page web applications. Appreciate any insights.

It's not an error in timeonsite.js nor a flaw in Angular app's routing module, but rather a crucial setting that seems to have slipped through the cracks during initialization of Single-page apps. Timeonsite.js continuously captures real-time timing data in web and mobile browsers; There are no interruptions or resets in tracking user stay time. It segments real-time sessions into pageviews and time-on-page based on page refreshes, reloads, or browser exits. However, for single-page/Angular apps utilizing "Hash-based" routing, a specific navigation strategy is employed. In such cases, where refreshes or reloads don't trigger, timeonsite.js interprets the user as staying on page 1 indefinitely. This necessitates the inclusion of custom setting trackHistoryChange: true,, which is vital for hash-based routing applications.

Hence, it appears that you may have overlooked trackHistoryChange: true,, a mandatory configuration for single-page/Angular apps. Any application relying on hash routing should incorporate this setting.

var config = {
    trackBy: 'seconds',

    ....
    ....
    ....

    trackHistoryChange: true, //mandatory setting for single-page app

    ....
    ....
    ....
}

Integrate this missing setting and try again. The expectation is that page titles and URLs will be accurately captured during navigation.

Documentation, TOS-configuration-option (Refer to section on Tracking single-page apps)

Answer №2

The reason for this behavior is that the sendBeacon function is being executed before the Angular route changes take place.

I suggest implementing it in the following manner:

app.component.ts:

this.router.events.subscribe((routerEvent) => {
  if (routerEvent instanceof NavigationEnd) {
    const url = routerEvent.url;
    // make the call at this point
  }
});

This updated url will reflect the changes.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is it possible to use Angular CLI 6 to run ng serve with Angular 4?

I have a project using Angular 4. I recently updated my Angular CLI version: Angular CLI: 6.1.5 Node: 10.9.0 OS: win32 x64 Now I'm wondering how to run ng serve for my Angular 4 project? However, I noticed that the file angular.json is missing in ...

How come my counter is still at 0 even though I incremented it within the loop?

Within my HTML file, the code snippet below is present: <div id="userCount" class="number count-to" data-from="0" data-to="" data-speed="1000" data-fresh-interval="20"></div> In my Ja ...

In the event that the API server is offline, what is the most effective way to notify users that the server is not accessible on the client-side?

I am working on a project where my website interacts with an API hosted on a different server. The website makes API calls and displays the data in a table. However, I want to implement a way to alert the user client-side using JavaScript if the API server ...

What could be the reason for the function providing the value of just the initial input?

Why am I only getting "White" when I click on the colors? I can't seem to get any other values to appear on the screen. I'm confused about what mistake I might be making here. var x = document.getElementById("mySelect").value; function myFunc ...

Securing special characters in cshtml

I am working on a razor view which includes a hidden field called Model.Token. The Token contains special characters that are then appended to a link in the href attribute. <a href='http://<a href="/cdn-cgi/l/email-protection" class="__cf_email ...

Cease the use of bi-directional data binding on an Angular directive

One way I've been attempting to send information to a directive is through the following setup: <ui-message data="{{app}}"></ui-message> In my controller, this is how I define it: app.controller("testCtrl", function($scope) { $scope.a ...

Accessing a factory's functions within itself in Angular

I'm really trying to understand how all of this operates. It seems like it should be working as intended. I have an Auth factory that, when the jwt token expires, calls its 'delegate' method which obtains a new token using the refresh token. ...

Finding the final day of a specific year using the moment library

When it comes to determining the last day of a year, hard-coding the date as December 31st seems like a simple solution. While there are various methods using date, js, and jquery, I am tasked with working on an Angular project which requires me to use mom ...

A guide on Implementing PastBack Functionality with AJAX Responses

When I make a GET request for an HTML page, I come across the following element: <a id="ctl00_cphRoblox_ClaimOwnershipButton" href="javascript:__doPostBack('ctl00$cphRoblox$ClaimOwnershipButton','')">Claim Ownership</a> My ...

Adjusting window size when page is resized

While browsing through SO, I stumbled upon this interesting piece of code: var w = window, d = document, e = d.documentElement, g = d.getElementsByTagName('body')[0], x = w.innerWidth || e.clientWidth || g.clientWidth, y = w. ...

Mui CardContent not displaying transparent background color properly

I recently completed a course that used MUI v4, and I'm now facing challenges with transitioning to v5. Despite my best efforts, I am struggling to replicate all the styles and find myself stuck. This issue relates to my FeaturedMovie.jsx component i ...

Adjust properties based on screen size with server-side rendering compatibility

I'm currently using the alpha branch of material-ui@v5. At the moment, I have developed a custom Timeline component that functions like this: const CustomTimeline = () => { const mdDown = useMediaQuery(theme => theme.breakpoints.down("md")); ...

The angular content is not scrolling

I have a basic angular content window that contains an adjustable group of settings values. When the window is collapsed, the fxLayout collapses properly, but I am having difficulty scrolling vertically. I have attempted to use overflow-y and just overflow ...

TypeScript creates a .d.ts file that contains declaration statements rather than export declarations

When compiling my code using the command tsc --p typescript/tsconfig.json --outFile "dist/umd/index.d.ts", I encountered an issue. The contents of my tsconfig.json file are as follows: { "include": ["../src/**/*"], "exclude": ["../**/*.test.ts"], ...

Incorporate vanilla JavaScript and HTML into a Next.js application

I am facing an issue where I have a file that contains JavaScript and HTML code, which needs to be rendered within a Next.js application. The challenge is that the code in the file cannot be converted to JSX, and React Helmet does not seem to render anythi ...

A Vue button that toggles between three states depending on the value in an array

In my Vue project, I am working on loading an array when the page loads. I need to check the status of each line item in the array and display a specific button for each status using a 3-way toggle. Although I believe I understand the main concept, I am s ...

Locate every instance where two arrays are compared in TypeScript

My goal is to search for matches in Object 2 where the _type corresponds to filterByCallTypeTitulo in Object 1, and then create a new array including all the matched information from Object 2. I attempted to achieve this using the filter() method and forE ...

The component is unable to access the injected service from a separate module due to its null

In my Angular v6 project, there are 2 modules: app.module.ts, which includes the AppComponent and BoatComponent, and imports another module called draggable.module.ts. app.module.ts @NgModule({ declarations: [ AppComponent, BoatComponent ...

How to use the handleChange() function in React with various state properties

Explaining a bit about the handleChange function, which takes the name of the state element to be associated with it. Is there a specific reason why it has to be structured like this: handleInputChange(property) { return e => { this.setSta ...

Using a for loop in JavaScript to fetch and display a specified number of results from an API

Recently, I delved into the world of Javascript and API arrays to grasp the concept of retrieving and manipulating various APIs. The topic of Javascript loops and arrays has been discussed numerous times, both here on StackOverflow and on other platforms. ...