skip to content

JavaScript: Recording Outbound Links using Ajax

A problem many webmasters encounter when adding paid (or unpaid) external links to their site is conflicting demands between what they want and what the person paying for the links might want.

For example, if you add a 'redirect' step between clicking on a link and going to their site then the HTTP_REFERER information can be lost, but how else can you record how many times a link has been clicked, by who and on which page? One answer - at least for modern browsers - is to use XMLHttpRequest (Ajax).

We have a new article Making an asynchronous request before following an href which provides more detail and other options for making Ajax requests when a link has been clicked.

Generating a server-side event

Start by including the Ajax script introduced in the related article: Avoiding the Race Condition with Ajax. We're not going to use any of the DHTML or form-handling methods this time, just the loadXMLDoc() function to initialise and make the request.

The first step is to include the AjaxRequest class:

<script src="ajaxrequest.js"></script>

Then we create a function that will pass the relevant information to a server-side script. Unlike earlier examples the target script doesn't need to have any output.

The person clicking on the link will presumably be leaving your website so any values returned would be lost anyway.

The parameter passed to the function is the target URL of the link being clicked. The function then generates an XMLHttpRequest passing this value (target) along with the address of the current page (location.href) :

<script> var record_outbound = function(target) { if(encodeURIComponent) { var req = new AjaxRequest(); var params = "src=" + encodeURIComponent(location.href) + "&target=" + encodeURIComponent(target); req.loadXMLDoc("record_outbound.php", params); } return true; }; </script>

What the script does with this information is up to you. Normally you would record the information in a database or text file for later processing. You might also want to increment a counter or send an email. If you send the data using GET rather than POST then the event will also be recorded in your server log.

Note that the return value of this function is always true. If you returned a value of false then the link clicked on would not be followed.

Upgraded Code

Here is the same function from above re-written to more modern standards and using our improved AjaxRequestXML.js JavaScript class for sending Ajax requests:

<script src="AjaxRequestXML.js"></script> <script> const record_outbound = (target) => { let params = {}; params.src = location.href; = target; (new AjaxRequestXML()).get("record_outbound.php", params); return true; }; </script>

You will see that we're not longer checking whether encodeURIComponent is supported in the browser as it is assumed. We also no longer have to escape the parameters being sent as that is handled internally by the new class.

But we still need to set up event handlers if we want to track clicks on external links.

Adding an event handler to external links

The simplest method is to add an onclick event handler to each link that you want to monitor:

<a href="" onclick="return record_outbound(this.href);">Link Text</a>

Now whenever one of these links is clicked in a JavaScript-enabled browser your server-side script will be triggered and the link then followed as normal. Because you're not messing about with redirects or other complications the target site will see a clean referer address and be able to record the same information that you already have.

If you want to be a bit less obvious, or reduce the amount of HTML that needs to be added for each link, you can use DHTML and an onLoad event to add the handler to selected links. The steps are then as follows:

  1. Assign a class to the links in question:
  2. <a class="external" href="">Link Text</a>
  3. Set up a function to be called onload to finds links with a class of 'external' and dynamically assign the onclick event handler to them:
  4. var add_record_event = function() { document.querySelectorAll("a.external").forEach(function(current) { current.addEventListener("click", function(e) { return record_outbound(this.href); }); }); };
  5. This function then needs to be called by either window.onload or a DOMContentLoaded event handler.

Other options for determining which links to add the event handler to might include: all links within a certain container (eg. a div with a specific id), those with target="blank" or those with a target address outside your domain (use a regular expression to test the href value of each link).

The approach we would use now is the following:

<script> window.addEventListener("DOMContentLoaded", (e) => { document.querySelectorAll("a[href^=http]:not([href*='the-art-of-web\.com/']").forEach((current) => { current.addEventListener("click", (e) => record_outbound(current.href)); }); }); </script>

The selector that is being used here matches all links having an href starting with 'http' where the href does not contain '' anywhere in the body of the URL.

This way all links on the page that don't go to other parts of The Art of Web will have the new onclick event assigned to them - sending data via Ajax when clicked.

Known Limitations

Obviously this method only works in Ajax-enabled browsers. That means that Javascript must be enabled and in some cases (MSIE 5.x) also ActiveX.

Before you get too carried away with this method for recording user actions, be aware that there's a possibility that browsers will be forced to change their behaviour in the future to better protect privacy. That could mean that users are alerted to the fact that an Ajax request is being made or given the option of blocking them entirely.

This isn't likely to happen too soon however as there are plenty of alternative methods such as using an IFRAME or 1x1-pixel image.

The main problem is that if you're not using target="_blank" then the whole process could be cancelled by the browser when the current page is replaced. To get around that you should read this article.

< JavaScript

Post your comment or question