«

»

Oct 28

Print this Post

Mouse middle-click and WebKit browsers

Recently, I worked on a bug reported by our user about broken middle-click functionality on one specific page. First what you do when you found such a bug report you are trying to reproduce it. I went to this page in production environment using Firefox and everything worked as expected. I went to this page served by my development machine and it worked. It seemed somebody, somehow fixed it (probably by accident because the bug report was not closed). But something told me to try it out also in Google Chrome. I started debugging it, later I showed it to my colleagues and one of them said: “I hate Google Chrome! It’s another IE but it’s IE of these days!”. I admit, he might have overreacted a little bit but it was not Google Chrome only issue. Later, we found out all browsers acts the same about middle-click except those ones based on WebKit engine.

WebKit browsers treat middle-click the same as left-click and fire click event. In example, they fire all functions bind to link via jQuery click() method.

Example

Imagine you want to track different events on a page to make sure everything works as you expected and to track how users use it in order to make the product even better for them. You are implementing tracking function similar to this one:

// File: tracking.js

//Event tracking function
function trackEvent(eventName, data) {
   if (data && data.href && typeof event != 'undefined') {
      event.preventDefault();
   }

   // Here goes logic responsible for choosing place where we store
   // tracking information and for storing those data

   //delay at the end to make sure all of the above was at least invoked
   if (data && data.href ) {
      setTimeout(function() {
         document.location = data.href;
      }, 100);
   }
}

The function accepts two arguments: eventName and data. First one could be our name of an event in example image-click, link-click, impression, scroll, hover. Second one is an object with different fields which are tracking data stored by us in our system. The function works in simple way: it gets the arguments, stores tracking data and mostly that’s all. This is its main task: to store given data. How does it gets the data? Simply, we use this function in our modules, new features on a page etc.

Imagine, you are releasing new menu on your page. You want to track all clicks in the new menu but you want to know which of those are clicks on sub-menu links and which are not. You also want to know how many users visits sub-pages of your page by using only the menu and not in example using search results or by typing a sub-page address directly in a browser’s address field. In order to do that, you are adding a small part of new code in your NewMenu.js file:

// File: NewMenu.js

NewMenu = {
   init: function() {
      //...
      $('#NewMenu a').click(function(e) {
         var target = $(e.target),
             href = target.attr('href');
 
         if( target.hasClass('submenu') ) {
            trackEvent('link-click', {
               feature: 'new-menu-submenu-element',
               href: href
            });
         } else {
            trackEvent('link-click', {
               feature: 'new-menu-main-element',
               href: href
            });
         }

         $.storage.set('entryPoint', 'newMenu');
      });
   },
   //...
}

And to your main.js file you put this simple mechanism which is tracking an entry point of a page:

// File: main.js

var entryPoint = $.storage.get('entryPoint');
if( entryPoint) {
   trackEvent('impression', {
      feature: 'new-menu-page-opened',
   });
   $.storage.set('entryPoint', null);
}

Except firing trackEvent() function with each anchor click in new menu the entryPoint variable is set in local storage of the browser. Then, after JavaScript is loaded on a subpage and entryPoint variable is found in local storage another trackEvent() call is fired to store data that a user used new menu to get to this page.

Let’s go back to the second parameter of the tracking function because this is the most important one here. Depending on the fact if data object has href field set or not our trackEvent() function will act differently. So, after a sub-page is loaded tracking function is called, it saves data to our tracking storage and that’s all. It doesn’t affect an end-user. But if the user clicks a link from #NewMenu container the data object passed to tracking function will have href field. And according to the code it will take 100 milliseconds and after that time load the page from data.href value. And this case affects the user, however, as long as he uses his mouse’s left button to click on a link the way he will be affected is only 100 milliseconds delay. If he uses his mouse’s middle button to click on a link and it all happens in Firefox or any other non-webkit browser it will only be a 100-milliseconds delay. But if he uses his mouse’s middle button to click on a link and WebKit browser he will get feeling his mouse’s middle button does not work on this page correctly.

Final thoughts

I am still not sure if in this case WebKit creators are “IE of these days” or not. On one hand it is annoying their engine does not handle middle-click as any other browsers do. But on another hand it is a click and a click event should be fired if you look at that from this point of view. At the end of the day, I have added one more parameter to data which is being passed to tracking function and we agreed to treat middle-click as a left click in our tracking statistics. Basically, it does the same but in a new browser tab. But I am also not sure about this solution once I read about which or button properties.

//File tracking.js

//Event tracking function
function trackEvent(eventName, data) {
   var data.button = data.button || false;
   
   if (data && data.href && typeof event != 'undefined') {
      event.preventDefault();
   }

   // Here goes logic responsible for choosing place where we store
   // tracking information and for storing those data

   //delay at the end to make sure all of the above was at least invoked
   if (data && data.href ) {
      setTimeout(function() {
         invokeDelayedClick(data.href, data.button);
      }, 100);
   }
}

function invokeDelayedClick(location, button) {
   if( button === 1 || button === 4 ) {
      window.open(location);
   } else {
      document.location = data.href;
   }
}

Permanent link to this article: http://blog.lukaszewski.it/2012/10/28/mouse-middle-click-and-webkit-browsers/

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>