6

I want to log all clicks on a link.

I've written a little logger, which can be called by an url (returns an empty page). This url is called with a jquery-ajax-method. But unfortunately not every click is logged, if the user uses firefox (everything looks ok in IE).

I've tried many things but have no solution to this problem, have anybody a glue?

HTML-Code:

<a href="http://google.com" onclick="return loggClick();">Click</a>

JS-jQuery-Skript:

function loggClick(){
   $.ajax({
        type: "POST",
        url: "Logger.ff", //dynamic url to logging action
        data: {
            sid: 'abc123' //random data
        },
        contentType: "application/x-www-form-urlencoded; charset=UTF-8",
        cache: false
    });
    return true;
}

EDIT: I've missed in the example that i have to pass dynamic parameters in the js-call, so it's "not possible" to remove the onclick-event :(

gamue
  • 161
  • 1
  • 8

5 Answers5

4

I would start getting rid of the inline 'onclick' code and binding the event later:

  <a href="http://google.com" rel="outbound" >Click</a>


   $("a[rel=outbound]").click(function(){ 
        var url = this.href; 
        $.ajax({
        async: false,
        type: "POST",
        url: "Logger.ff", //dynamic url to logging action
        data: {
                sid: 'abc123' //random data
                clicked: url
        },
        contentType: "application/x-www-form-urlencoded; charset=UTF-8",
        cache: false
     });
     return true; 
  }); 

Also, you might have a "Race Condition" occurring. In my example I have set async to false.

This will stop the function returning and following the link before the request has been performed.

About Async

The reason I use async: false here is because with the default, aync is true, which means the AJAX request may only be partially transmitted by the time the browser sees return: true, and navigates away from the page.

This is that "race condition" I was mentioning. Here, its a cheap trick that avoids it by making the browser essentially come to a halt until the request is complete, and then allowing the browser to click the link.

A more sophisticated answer would be having it return "false", ( which will kill the browsers native "follow link" behaviour ), and then having the real redirection performed in the complete function.

This is not without its own issues mind, and could result in slow browsing behaviour while requests complete, allowing users time to click other links, which appear to do nothing, and then eventually one request gets through at a random time, and a seemingly random redirection occurs.

Hence, advanced solutions include blocking the rest of the page and giving some sort of progress indication while the request completes.

( And hence, the complexity of this solution is an order of magnitude harder to pull off in a simple example than async: false )

Kent Fredric
  • 56,416
  • 14
  • 107
  • 150
  • +1 I like this, although the OP would have to have control of the server-side code to perform the location-redirect right? – bendewey Mar 19 '09 at 14:46
  • @bendewey what? your last part made no sense. – Kent Fredric Mar 19 '09 at 14:48
  • Why is this better? Is something wrong with the onclick? Do you think it will solve the problem or do you suggest it only because it is better? – Josef Sábl Mar 19 '09 at 14:48
  • its is better, there's 2 parts solution here. But making the code "right" as well as "working" is always a good 2-for-one deal – Kent Fredric Mar 19 '09 at 14:52
  • 1
    Also, I've seen weirdnesses with the inline way that didn't make sense, so I generally avoid them like the plague. – Kent Fredric Mar 19 '09 at 14:53
  • my fault, I didn't notice the async=false part, I thought you were passing the url to the logger.ff to do a redirect. totally read your post wrong – bendewey Mar 19 '09 at 14:54
  • @kent fredric, quick question why do you use outbound and not nofollow? – bendewey Mar 19 '09 at 16:24
  • because nofollow doesn't do what you think it does. nofollow is abused by SEO people for the wrong things. rel is an attribute describing the links relation to the page. thus, "nofollow" is sematically meaningless. – Kent Fredric Mar 20 '09 at 01:45
  • Adding the async false seems to make Chrome 41 blocking the opening of the link (target blank) like if it was a popup. – NLemay Apr 01 '15 at 18:02
  • You really shouldn't use `async:false` in production. It was just a convenient way to make the problem easier to diagnose. ie: If the browser redirected prior to the background async connection completeing, code could break and your server could lose the transmission. Hence, I blocked until an OK was recieved, and then signaled to browser "Ok, redirection is now fine". But there are probably better ways. – Kent Fredric Apr 06 '15 at 20:50
  • @NLemay I have no greatly expanded on my use of async controls, hopefully it will be informative. – Kent Fredric Apr 07 '15 at 14:03
4

I think the reason FF is giving you poor results is because you're leaving the page before the action takes time to execute. As mhartman's link mentions if you use a target on your external link it should work fine. If you can't do that then you may have to wait for the log to complete, although you may see delays in navigation.

HTML code

<a href="http://google.com" onclick="return loggClick(event);">Click</a>

the

function loggClick(e) {
  if (!e) e = window.event;

  e.preventDefault();  // cancels the link

  var theLink = this.href;  // stores the link for later

  $.ajax({
     async: false,
     type: "POST",
        url: "Logger.ff", //dynamic url to logging action
        data: {
            sid: 'abc123' //random data
        },
        contentType: "application/x-www-form-urlencoded; charset=UTF-8",
        cache: false,
         complete: function() {
           // navigate when the log completes
           this.location.href = theLink;
         }
    });
    return true;
  }
}
Cory House
  • 14,235
  • 13
  • 70
  • 87
bendewey
  • 39,709
  • 13
  • 100
  • 125
1

A second more server side approach that you may not have thought of would be to have a page that handles your redirects and logs the data then.

For example:

<a href="LoggerRedirect.ff?url=http://google.com">Click</a>
bendewey
  • 39,709
  • 13
  • 100
  • 125
  • I've implemented something like this on one of my sites, the problem is that its really hard to sort the "noise" out of the click logs. I found that there was about a 50:1 ratio of spider clicks to human clicks. AFAIK most spiders don't bother loading a JS interpreter for each page they visit, so using JS to log outbound clicks seems reasonable enough. – prairiedogg Oct 23 '09 at 05:37
0

This example will fire off an ajax post before the link click is processed. I found I had to call ajax synchronously or the POST will not be executed. In this example my ajax post is to a PHP page which appends a log file. This example is efficient since it only attaches a click event to the window. It also will not interfere with a user clicking a link.

//attach click event to window
attachClickEvent = function() {
    $(document).on('click', function(e) { //document for IE8 compatibility
        var element = $(e.target);
        var tag = element.prop('tagName');

        if (tag === 'A') {
            var data = {'title':document.title, 'URL':element.attr('href'), 'origin':location.href};
            logData(data);
        }
    });
}

logData = function(data) {
    $.ajax({
        async: false,
        cache: false,
        type: "POST",
        data: data,
        url: 'logger.php',
        success: function(msg) { 
            //debugging
        },
        error: function() {
            //debugging
        }
    });
}
mbokil
  • 3,202
  • 30
  • 22
0

The above poster is correct, it is unreliable because you are leaving the page before it has a chance to log it.

You could do this:

1) return false, so the href isn't active. 2) log the click 3) use location.href to redirect to the url it would have been redirected to

you might see a slight delay if it takes long time for your onclick handler to execute.