How dangerous is e.preventDefault();, and can it be replaced by keydown/mousedown tracking?

I'm working on a tracking script for a fairly sophisticated CRM for tracking form actions in Google Analytics. I'm trying to balance the desire to track form actions accurately with the need to never prevent a form from not working.

Now, I know that doing something like this doesn't work.

$('form').submit(function(){
 _gaq.push(['_trackEvent', 'Form', 'Submit', $(this).attr('action')]);
});

The DOM unloads before this has a chance to process.

So, a lot of sample code recommends something like this:

$('form').submit(function(e){
e.preventDefault();
var form = this; 
 _gaq.push(['_trackEvent', 'Form', 'Submit', $(this).attr('action')]);
//...do some other tracking stuff...
setTimeout(function(){
form.submit();
}, 400);
});

This is reliable in most cases, but it makes me nervous. What if something happens between e.preventDefault();and when I get around to triggering the DOM based submit? I've totally broken the form.

I've been poking around some other analytics implementations, and I've noticed something like this:

$('form').mousedown(function(){
 _gaq.push(['_trackEvent', 'Form', 'Submit', $(this).attr('action')]);
});
$('form').keydown(function(e){
    if(e.which===13) //if the keydown is the enter key
    _gaq.push(['_trackEvent', 'Form', 'Submit', $(this).attr('action')]);
});

Basically, instead of interrupting the form submit, preempting it by assuming that if someone is mousing down or keying down on Enter, than that form is submitted. Obviously, this will result in a certain amount of false positives, but it completely eliminates use of e.preventDefault();, which in my mind eliminates the risk that I might ever prevent a form from successfully submitting.

So, my question:

  • Is it possible to take the standard form tracking snippet and prevent it from ever fully preventing the form from submitting?
  • Is the mousedown/keydown alternative viable?
  • Are there any submission cases it may miss? Specifically, are there other ways to end up submitting besides the mouse and the keyboard enter? And will the browser always have time to process javascript before beginning to unload the page?

Answers


Them fellers over at that there Googleplex are awful bright and they figgered some day somethin' like this was bound to happen and now, sure enough, it has. Why don't you give this a good try:

$('form').submit(function(e){
  e.preventDefault();
  var form = this; 
  _gaq.push(['_trackEvent', 'Form', 'Submit', $(this).attr('action')]);
  //...do some other tracking stuff...
  _gaq.push(function(){
     form.submit();
    });
  });

That _gaq.push thigamajigger executes its elements sequentially, so you should be jest fine.

And no, I don't know why I suddenly started talking like this.


I use a different approach, and generate the event tracking script in the page resulting from the submit. You could call it deferred event tracking.

I wrote a blog post with all details about my approach to event tracking in backend actions. It is biased towards Java-Struts, but you can get the general idea.

The rationale is that I want to track some things after they happened at the server side. In this kind of case, after the form was submitted and processed by the server.

What I do (very summarized):

  • Store events in an object tied to the session (a list/queue)
  • Flush these events upon the next page render (generate the javascript and empty the queue)

If you must have forms always work but tracking can be sacrificed if absolutely necessary, you could just try/catch it.

$('form').submit(function(e){
    try{
        e.preventDefault();
        var form = this; 
         _gaq.push('_trackEvent', 'Form', 'Submit', $(this).attr('action'));
        //...do some other tracking stuff...
        setTimeout(function(){
            form.submit();
        }, 400);
    } catch (e) {
        form.submit();
    }
});

e.preventDefault() doesn't have to be right at the beginning of the function call. Why not just have an if statement to verify if everything is working correctly, and only call e.preventDefault() if it does. If any function in the chain doesn't return the expected result, set a submitted variable to false, and don't prevent the default.

This might be a little more difficult to handle when it comes to anything asynchronous (such as your setTimeout, but there will be ways to make fairly sure, depending on what your code looks like. So you can check if methods exist with if (_gaq.push) (for example). You can't make 100% sure without testing every single combination in every browser, but I reckon you can get a pretty satisfactory result with this.


Another approach:

var pageTracker;
_gaq.push(function() {
    pageTracker = _gat._getTrackerByName();
});

$('form').submit(function(e){
    pageTracker._trackEvent('Form', 'Submit', $(this).attr('action'));
};

I would guess this way _trackEvent would be synchronous but I haven't tested it.


Is there a reason why you can't just issue the call to Google Analytics from the server side based on the POST that it receives?

I don't know what your system is built in, but for instance, this PHP project would issue a call to GA, thus removing the problem of the DOM unloading, the need to break the form, and so on.

http://code.google.com/p/serversidegoogleanalytics/

I realise that you might need to capture a cookie - you might write the value into a hidden field in the form before it is submitted?


To expand on @Malvolio's answer: Once gaq sends the event the next item in the queue will process. This means that the event HTTP request may abort on the client side, but GA will receive the request. Don't worry about blocking script execution until the response finishes. It is a fire and forget scenario.


Need Your Help

jQuery Mobile changePage()

php javascript jquery redirect

From this.php, if you click a button, changePage(#here) will execute.

SqlAlchemy Dynamic Where

python sqlalchemy

I would like to dynamically select the column I am querying so for example:

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.