Delay loading of Google Analytics / Google Tag Manager script for better PageSpeed score and initial load

So I was recently doing some performance optimizations on a website, ensuring it loads as fast as possible, and that Search Engines like Google prioritize it over the competition.

I came across the “Add Expires headers” recommendation and was sad to realise we only scored “F” (i.e. 1/100 points):

It’s funny that Google punishes it’s own scripts, nevertheless, being the perfectionist I am, I had to come up with a solution.

To my surprise it was difficult for me when researching what others have done here. Most questions are about delaying the triggering of a specific Google Tag Manager tag, but that does not prevent the gtm.js / analytics.js script from being loaded, and thus results in a lower PageSpeed score.

The problem

First of I tried to add “defer” to the script, meaning that it will first load, when everything else on the page has finished parsing. However, it didn’t change much. There is also some issues that arises when you delay loading of the analytics / Google Tag Manager script (or other tracking scripts for that matter): less accurate results. Why? Because if the user leaves the page, before the script has been loaded, page views / events would not be tracked on that page.

My solution:

To ensure the most accurate tracking statistics while keeping a high PageSpeed score, I came up with the following conditions for when the Google Tag Manager script should be appended

  • 3500 seconds after documenthas fired its DOMContentLoadedevent
  • When user scrolls the page (PageSpeed test does not trigger scroll/click/mousemove events)
  • When user moves the mouse on the page
  • When user touches anywhere on the page (for mobile devices)

This seemed to load the tracking script, and PageSpeed tests would not penalize me for missing “Expires Headers”.

Also, it would keep the amount of missing analytics to a minimum, because as soon as the user would move the mouse, scroll or interact with the page in any way, the tracking script would be initialized right away. Only scenario where the visitor would not be tracked, is if a user visits the webpage and decides to leave before ~4 seconds AND not interacting with the website in ANY way. I’m not sure such a visit would have any real value to the marketing team anyway — we’ll see.

However, there was still one big issue: PageView events would not be tracked. I had to figure out why it didn’t register the PageView event when script was added to the page later. If the script is added while the page is still loading, the PageView event gets fired. However, if it’s added later (say 3 seconds after page has finished loading) no events are fired. Google Tag Manager’s “Preview Mode” was a great tool in helping me figure out what triggers that fired which tags.

I realized PageView would be triggered on a specific event called `gtm.js`, so my solution was to add an onLoadcallback to the tracking script, pushing this event manually into the dataLayerobject.

Voila! It now works, and PageSpeed scores are higher than ever ūüėČ

The code

document.addEventListener('DOMContentLoaded', () => {
	/** init gtm after 3500 seconds - this could be adjusted */
	setTimeout(initGTM, 3500);
document.addEventListener('scroll', initGTMOnEvent);
document.addEventListener('mousemove', initGTMOnEvent);
document.addEventListener('touchstart', initGTMOnEvent);

function initGTMOnEvent (event) {
	event.currentTarget.removeEventListener(event.type, initGTMOnEvent);

function initGTM () {
	if (window.gtmDidInit) {
		return false;
	window.gtmDidInit = true;
	const script = document.createElement('script');
	script.type = 'text/javascript';
	script.async = true;
	script.onload = () => { dataLayer.push({ event: 'gtm.js', 'gtm.start': (new Date()).getTime(), 'gtm.uniqueEventId': 0 }); }
	script.src = '';


Nested layouts in Nuxt/Vue.js

If you’re like me, and like to keep things tidy and prefer not to repeat yourself, you will most likely find yourself needing to nest layouts when developing a Nuxt application.

This is luckily a fairly easy thing to do, expect you need to do a bit of tweaking.

Assuming you haven’t modified your layouts/default.vue file, it will most likely look like this:

// layouts/default.vue
		<nuxt />

If we’d like to apply nested layouts, we could take advantage of slots and do something like this:

// layouts/default.vue
		<nuxt v-if="!$slots.default" />
		<slot />

Then we could add a new layout, like so:

// layouts/newLayout.vue
		<h1>Surrounding layout</h1>
		<nuxt />
import DefaultLayout from '~/layouts/default.vue';

export default {
	components: {

Congratulations. You now have a nested layout. In any file you’d like to use the nested layout, you simply set your layout like so:

export default {
	layout: 'newLayout' // name of your new layout

Clear expired/invalid data from wp2_icl_translations

I managed to clear out 18.000 unused / invalid rows from the `wp2_icl_translations` table, after realizing a lot of old/expired translations was present in the table. This can happen if you’re doing huge import/exports to your site, or delete products/pages/posts manually (via SQL) and also are using WPML.

Run below query on your database to delete unused rows (rows that point to a page/post that no longer exists in the database). Remember¬†to¬†backup¬†your¬†table¬†before¬†doing¬†this,¬†even¬†though¬†I¬†would¬†say¬†it’s¬†pretty¬†safe¬†to¬†run¬†it.

DELETE wp2_icl_translations FROM wp2_icl_translations
LEFT JOIN wp2_posts p ON = element_id
WHERE element_type IN ('post_product_variation', 'post_product', 'post_post', 'post_page') and is null

If you’d like to see which rows would be deleted first, you could run this query and have a look at the results:

SELECT element_id, element_type, as post_id, p.post_title, p.post_type, p.post_date FROM wp2_icl_translations
LEFT JOIN wp2_posts p ON = element_id
WHERE element_type IN ('post_product_variation', 'post_product') and is not null

Cloudways Breeze cache plugin with WPML Multi Currency switcher

If you are using Cloudways and/or their caching plugin Breeze you might have issues if you’re running a WooCommerce shop with WPML Multi Currency Switcher – at least I did, after migrating to Cloudways and switching away from W3 Total Cache.

The issue will only appear to non-admins, or guests/visitors visiting your site without being logged in (where cache is then not enabled, as per how Breeze works).

After a bit of researching I realized that this had something to do with WPML and how they work, and found below filter to be working. Add it to your themes functions.php file or anywhere you would add custom coding to your WordPress installation.

    add_filter('wcml_is_cache_enabled_for_switching_currency', function($cache_enabled) {
        return true;

How to remove admin bar css (margin-top) in WordPress 5

The WordPress admin bar is outputting below HTML code to add margin-top to the html and body element of the page.

<style type="text/css" media="print">#wpadminbar { display:none; }</style>
<style type="text/css" media="screen">
	html { margin-top: 32px !important; }
	* html body { margin-top: 32px !important; }
	@media screen and ( max-width: 782px ) {
		html { margin-top: 46px !important; }
		* html body { margin-top: 46px !important; }

However, if you like to have full control over what’s happening and perhaps fixing this in your own stylesheet and thereby avoding unnecessary HTML, you can add this nifty line to your functions.php file (in your theme or plugin folder):

add_theme_support( 'admin-bar', array( 'callback' => '__return_false' ) );

Previously you could have this code, but it is no longer working:

add_action('get_header', function() {
	remove_action('wp2_head', '_admin_bar_bump_cb');

You’re welcome ?

How can I make my wired headphones/earplugs wireless? I stumbled upon a magic trick.

If you’re the guy like me who prefers everything to be cable less, or if you just have a nice pair of speakers that does not support bluetooth, there’s actually a cool trick you can do.

For a few dollars you can buy a device called Trond. Trond is a small battery powered bluetooth device thats able to send and receive audio via Bluetooth.

So for instance, my old jack-wired headset now works on my iPhone X that doesn’t even have an jack input. Instead I have cabled my Tron to my headset, and then configured Tron to send the audio to my iPhone X.

Let’s say you have a nice old speaker system at home that currently only has a jack cable, you could also simply connect your Tron to your speaker system and have it Bluetooth ready in no time.

Only disadvantage I have found so far is that the sound quality is not that good. In a small test I made, the reach is a little better than the built into Bluetooth device in Bose SoundLink III, but sound quality is really not that good. I am however in contact with Trond customer service who will either refund me or give me a device that doesn’t have sound quality issues.

What other cool ideas could you come up with using Trond or a similar Bluetooth sender/receiver? Let me know in the comments section.

How do I make a screen-recording?

Anyone can record the screen easily.

If you prefer not to download any additional software to record your screen, chances are you already have a program that can do it for you. Luckily, if you’re on Mac, you can simply open QuickTime Player. On Windows you’ll need PowerPoint, or other download alternative (free) software – but don’t worry, this is easy.

On Mac OS

Step 1:¬†Open QuickTime Player (installed on every mac), and from the top menu bar, click [keybt]New Screen Recording ‚ĆÉ‚ĆėN[/keybt].

Step 2:¬†Ensure you use the Internal Microphone (then your voice may be heard).¬†Optionally, you may also tick “Show Mouse Clicks in Recording”.

Step 3: You’re ready to record your screen. Click the big red recording button.
You’ll be able to choose whether you wish to record you whole screen, or only the part of the screen.

Step 4: End the recording by clicking the stop button in the menu bar.

On Windows

The latest versions of PowerPoint include the feature to make a Screen Recording.

How to record your screen using PowerPoint on Windows

Step 1:¬†Go to the ‚ÄúInsert‚ÄĚ tab, and select ‚ÄúScreen Recording.‚ÄĚ

Step 2:¬†Choose ‚ÄúSelect Area‚ÄĚ to choose the specific area of your screen you want to record. If you want to record the entire screen, press¬†[keybt]Windows Key +Shift + F[/keybt]

Step 3:¬†Click the ‚Äúrecord‚ÄĚ button,‚ÄĚ or press[keybt]Windows Key +Shift + R[/keybt]

When you’re done you can save the video as a separate file to access or embed as you see fit.

Don’t have PowerPoint? Check out these alternative apps

If you don’t have PowerPoint check out¬†OBS Studio (free)¬†or¬†Snagit (paid)

Bitcoin and cryptocurrencies is for criminals

Many people I meet say: “bitcoin is for criminals” — maybe because they’ve heard that it has been used to buy drugs on deep-web services such as Silkroad. Just like criminals use Danish Kroner or U.S. Dollars to trade illegal things with.

Heres’ some wise reasons why I believe the title of this post couldn’t be further away from the truth.

  • Of all the currencies that criminals have preferences for the U.S. Dollar is king.
  • It is way more difficult to do money-laundering with Bitcoin compared to e.g. U.S. Dollar
  • Bitcoin is a tool to safe humanity from tyrans and oligarchy (the real criminals), today just covered up as a “get rich scheme”
  • Mafia clans in random countries such as Russia, Venezuela, Zimbabwe and so on uses U.S. Dollars
  • Bitcoin, per the specifications today, is probably the only monetary system that is most anti-criminal, given how open it is.
  • “Yeah but cryptocurrencies are anonymous and that enables criminal behaviour”, she said, handing a ‚ā¨50 bill over the counter at Starbucks that only hours before was used to buy meth cut with knock-off ritalin from a dirty cop outside an underage nightclub”

Data, data, data…

Given we have data on everything when using Bitcoin, in the case that we need to investigate a serious crime, we have very good data analysts and tools, to solve the issue, of even the most complex money laundering going on.

A crime done with cash would be untraceable. One in bitcoin would not.

For further reading, have a look at¬†Why Criminals Can’t Hide Behind Bitcoin

Serious macOS High Sierra security vulnerability – root user with no password

A serious vulnerability has been found in macOS¬†High Sierra – something I have no clue how could have ever happened. The main problem with this issue is, that it really doesn’t require any computer knowledge at all to take advantage of.¬† Your grandparents can do it, given they are able to move the mouse and use the keyboard (most non-techie users I know these days have finally learned this).

For some odd reason, the user “root”¬† has no password!!!

This means: anyone with access to your computer can login as administrator using no password. The username is¬†root and the password is blank. Blank is in total blank. Not “blank”. Yes. That’s right. BLANK.

What can a root user do?

  • Access your other user accounts
  • Disable Local Disk Encryption (FileVault)
  • Disable firewalls
  • Basically, he is¬†GOD on your computer, and can do whatever he wants.

What you need to do

Apple is working on a fix, but there’s no time to wait. Please go ahead and change the root password to a strong password. See Apples Guide how to.

Some people advise to disable the root account, but this is misunderstood. If you disable the root account, you can reenable it again using a failed login attempt. Whatever you enter as password in the failed attempt, will now be the new password for the root user.

If you’re familiar with the terminal, you can simply run this command:

cat /dev/urandom | env LC_CTYPE=C tr -dc a-zA-Z0-9 | head -c 60 | xargs -I rootpw sudo dscl . -passwd /Users/root rootpw

This is serious.

It is even possible to remotely access your MacBook if you  have this feature enabled, so go do something about it.