Technology entrepreneur, web analytics specialist, Internet enthusiasts. I also like movies, computer games, history and other interesting stuff. I was born in Guangzhou China and I live in Sydney Australia now.

Form Abandonment Tracking with Google Analytics

Forms are always the pain points in the conversion path, and form abandonment analysis is the key to find out which field causes the most pain and stops people going down the conversion path.

To be able to do form analysis, first thing you will need is to track which fields are touched by the visitors, and by aggregating the data you can find out the field causing most people to abandon the form. The usual way is to put a piece of Javascript code on the form field HTML element to trigger a tracking request when the mouse is pointed in the field. By tracking each field the visitor touched, you can see which is the last field the visitor stopped at.

The company website I work for has countless fields, that makes putting code on the form fields a mission impossible. Well, it could be done, but it might take a year considering our deployment cycles and tasks in the back log. But, I have tag management system, which I can insert Javascript bypass the deployment cycles. So I wrote this piece of Javascript which can find all the forms on the web pages, and inject the tracking Javascript on all the form fields. The following code is an example, which will trigger a Google Analytics event when someone clicks on a form field. It uses the Universal Analytics code syntax so if you are still running the older version of GA code, you will have to update the event tracking code in the example to make it work for you.

Once the code is implemented, you will find a event called “form-abandonment” in your Google Analytics event reports, and drill down by form ID then form field ID. So the forms and fields must have HTML element IDs. If you want to build a funnel with the form fields, you can change the code to trigger virtual pageview tracking requests.

Let me know what you think about it, or if you have any question feel free to contact me.

_fieldTracking = {}; = /*@cc_on!@*/false;
_fieldTracking.focusedFormFields = new Array();
_fieldTracking.formFieldTriggersActivated = false;
_fieldTracking.setFormFieldTriggers = function(){
	var forms = document.getElementsByTagName('form');
		var form = forms[i];
		var inputFields = document.getElementsByTagName('input');
			var field = inputFields[j];
			if(field.type != 'hidden') _fieldTracking.addListener(field, _fieldTracking.trackFieldFocus);
		var selectFields = document.getElementsByTagName('select');
			var field = selectFields[j];
			if(field.type != 'hidden') _fieldTracking.addListener(field, _fieldTracking.trackFieldFocus);
		var textFields = document.getElementsByTagName('textarea');
			var field = textFields[j];
			if(field.type != 'hidden') _fieldTracking.addListener(field, _fieldTracking.trackFieldFocus);
_fieldTracking.addListener = function(element, func) {
		element.attachEvent('onfocus', func);
		element.addEventListener('focus', func, false);
_fieldTracking.trackFieldFocus = function(e){
		var currentElement = ( ? e.srcElement :;
			var p = _fieldTracking.focusedFormFields.indexOf('#';
			if(p == -1){
				_fieldTracking.focusedFormFields[_fieldTracking.focusedFormFields.length] ='#';
				ga('send', 'event', 'form-abandonment',,;
if(typeof _pageId != 'undefined' && _pageId.indexOf('quote')>=0){
	if(!_fieldTracking.formFieldTriggersActivated) _fieldTracking.setFormFieldTriggers();
	_fieldTracking.formFieldTriggersActivated = true;

How Many Websites Are Using Google Analytics Or Adobe Omniture?

I guess this is a common question in the minds of people who work in web analytics. NerdyData is a website can give us a peek into the answer. It can search Javascript code snippet on websites, which means if we search for code unique enough to identify the web analytics solution, we can see how many websites are running with Google Analytics code snippet or Adobe Omniture code snippet. So I did that, and found a very interesting answer.

16,641,486 websites loads Google Analytics code library which means they are using Google Analytics according to this search result:!/

And 265,995 websites has s.pageName on the web pages which indicates they are using Omniture SiteCatalyst according to this search result:!/searchTerm=s.pageName/searchPage=1/sort=pop

Considering Omniture is so expensive, it is actually quite a big number. Note that one client could have multiple websites so it is not saying that Omniture has 265,995 clients. Anyway, very interesting figures.


Using JQuery and AJAX to Upload Files

I am building a field type to allow users to upload files and images in my CMS. So I need to figure out how to build a nice and neat interface to handle file uploading. Here is a prototype I built which allows users to select a file, and the file will be listed on the page with an upload button next to it. By clicking the upload button, the user can upload the file with a thread in the background without form submission. This is how it looks like after more work has been done to make the prototype look better with Bootstrap. So the prototype code below won’t look as pretty as the screenshot.

jquery upload files

To submit the file in the background, I take a shortcut using jQuery and the jQuery form plugin from So you will need to add the following code in the head section of the web page to load the Javascript library files.

<script src=""></script>
<script src=""></script>

The idea is to put a button on the page, and use Javascript to attach a hidden file upload form at the bottom of the page, and draw the file name with a upload button in a canvas layer on the page. Here is the code for the button and canvas.

<input value="select a file" onclick="selectFile()" />
<div id="fileCanvas"></div>

When the button is clicked, we call the selectFile() function to attach the hidden upload form, and click on the hidden file choose button.

var fileIndex = 0;
function selectFile(){
	var form = document.createElement("form");
	form.setAttribute("action", "/upload.php");
	form.setAttribute("style", "display:none;visiblity:hidden");
	form.setAttribute("method", "post");
	form.setAttribute("enctype", "multipart/form-data");
	form.setAttribute("id", "form"+fileIndex);
	form.setAttribute("name", "form"+fileIndex);
	var fileInput = document.createElement("input");
	fileInput.setAttribute("type", "file");
	fileInput.setAttribute("name", "field"+fileIndex);
	fileInput.setAttribute("id", "file"+fileIndex);
	fileInput.setAttribute("onchange", "handleFileSelection('"+fileIndex+"', this.files)");

If a file is selected, it will trigger the onchange event of the hidden file choose input and call the function handleFileSelection(). The function will then draw a layer within the canvas to display the selected filename and an upload button.

function handleFileSelection(id, file){
	var canvas = document.getElementById("fileCanvas");
	var fileDiv = document.createElement("div");
	fileDiv.setAttribute("id", "div"+id);
	fileDiv.innerHTML = file[0].name;
	// draw buttons
	uploadButton = document.createElement("input");
	uploadButton.setAttribute("type", "button");
	uploadButton.setAttribute("value", "upload");
	uploadButton.setAttribute("id", "uploadButton"+id);
	uploadButton.setAttribute("onclick", "uploadFile('"+id+"')");

And clicking the upload button will trigger the function uploadFile() to upload the file to a script called upload.php. The PHP script will then save the file if no error and response the result in JSON format so the page can disable the upload button if upload is successful.

$response = array();
	if(count($_FILES) > 0){
		foreach($_FILES as $fieldId => $value){
			if($value['error'] == 0){
				move_uploaded_file($value['tmp_name'], '\\upload\\'.uniqid());
				$response['status'] = 'success';
				// see php manual website for error code meaning
				$response['status'] = 'error';
				$response['errorCode'] = $value['error'];
		// exceed max post size
		$response['status'] = 'error';
		$response['errorCode'] = -1;
	$response['status'] = 'error';
	$response['errorCode'] = -10;
echo json_encode($response);

Javascript to Identify Landing Page

In web analytics implementation, we often need to implement some special tracking code for the landing pages. For example, firing adserver tags, applying some logics to the campaign code, etc. I just created this piece of Javascript code that can detect the referral URL hostname, and compare it against the current page hostname to check whether the referral URL is on the same website. If they are different, then I consider it is a landing page view because the traffic is from outside of the current website.

Here is the code:

if(location.hostname != document.referrer.match(/\/\/([^\/]*)\//)[1]){
	console.log('this is a landing page');
	console.log('not a landing page');

To test the code, you can copy and paste it in the browser console and execute it.


Standing Desk Solution

I spent the weekend moving around the furnitures in my study room, bought new shelves from IKEA. Now I have a standing desk for my laptop, and I can work on my new products without sitting any longer after sitting for a whole day in office.

Standing desk solution

Yes, the screens on the desk are slightly too high. I am thinking to buy a dual monitor desk mount to make it more adjustable to fit the height of my eyes. That should fix this minor problem.


Why There Are “Not Provided” and “Not Set” in Google Analytics Keywords Report


search keyword report 1One of the most common questions of Google Analytics is that why there is “not set” or “not provided” keywords in the keywords report. Although there are a lot of blog posts and discussions around this topic, I would like to dig into it deeper here.

“Not Provided” in keywords

You might already know the cause of it. The “not provided” keyword is caused by Google secure search, which Google introduced to protect privacy a while ago. Google secure search means that the searches are done with HTTPS instead of HTTP. When the destination page is with HTTP protocol, the referrer is not available in the DOM object. Therefore the analytics code is not able to retrieve the referrer. To resolve the problem, Google inserts an HTTP redirect link between the secure search results and the destination URL, so you can still see the referrer is Google with your analytics code. However, Google hides the search terms in the redirect link. That is the reason we cannot see the organic search terms of secure search traffic. It is a decision made by Google. They still want you to know that the traffic is from Google search, they just don’t want to give away the search terms. This only happens to organic search. You should not see “not provided” keyword in your paid search report.

So, who are the visitors likely to use Google secure searches? Google users who logged in to their Google accounts use secure search by default. More and more browsers start to implement secure search as default search method, for example, Firefox and Safari on iOS. In my experience, I have seen the percentage of “not provided” keyword increased a lot since it was introduced. In the organic search report of one of my websites, it is over 75%. And I expect the percentage to continue going up.

There is still a place where you can see what keywords are driving organic search traffic to your websites: Google Webmaster Tools. You can connect your Google Analytics profiles with your Google Webmaster Tools, to import the data into your Google Analytics. However, the data cannot be correlated with user behaviour data and conversions captured by Google Analytics. So, it is quite difficult to analysis the organic keywords’ performance (e.g. conversion rate).

“Not Set’ in Keywords

In some cases, you could see “not set” in your keywords report. There are a couple reasons for that to happen. If you look at the Google official explanation, it says that is because the traffic source is direct or referral. Well, it is not a very helpful answer when you see “not set” keyword in your search traffic source reports.

The typical reason for “not set” keyword in search traffic source reports is caused by Adwords auto-tagging. When auto-tagging is enabled, Google appends the “gclid” parameter to the destination URLs in Adwords ads. The value of the “gclid” parameter is a random string no one can understand except Google Analytics. Google Analytics can look up the campaign, keywords, etc from Adwords backend based on the “gclid” value. But, before Google Analytics conducts the look up, the owner of the Google Analytics profile and the Adwords account must authorize the integration, otherwise Google Analytics cannot access the data. So, without authorization of the integration, Google Analytics captures the “gclid” parameter, knows the visits are from Adwords campaigns, but it has no further information about the campaigns, like keywords, ads content, etc. Therefore, in the reports, the keyword is “not set”. To confirm auto-tagging is the cause, simply apply “campaign” as second dimension in the paid search report as the screenshot shows below, you can see the campaign is also “not set”.

search keyword report 2

To fix the problem, there are two options. The best option is to authorize the integration between Google Analytics and Adwords, so you can see the campaign and keyword performance in Google Analytics. The other option is turn off auto-tagging, and manually tag the destination URLs with UTM variables.

I hope this article gives you some good ideas about the “not provided” and “not set” keywords. If it still does not resolve the mystery in your keywords report, leave a question and I will try to answer it.

© Copyright 2014, All Rights Reserved