Problem/Motivation
When using multiple top-level domains, the Javascript for google_analytics attempts to match the hostname of a given href of a link against the defined set of top-level domains to determine whether a link off-domain should be registered as a link or as an outbound link event. However, the JS does not correctly check the href, and so a link will never be registered as a cross-domain link appropriately.
The relevant piece of code is:
// where $(this) is an <a> element
// http://drupalcode.org/project/google_analytics.git/blob/refs/heads/7.x-1.x:/googleanalytics.js#l42 for code in context
if (ga.trackDomainMode == 2 && isCrossDomain($(this).attr('hostname'), ga.trackCrossDomains)) {
// run _link()
}
The problem with this is that 'hostname' is not a valid or standard attribute of an <a> element, and there is no apparent code within google_analytics that would place this non-standard attribute on all links. Thus, the first argument of isCrossDomain() is always null, and so the check for cross-domain links always fails.
Additionally, isCrossDomain() checks against domain literals; it should probably be checking the top-most domains given, to allow for subdomain links being possible cross-domain link candidates. This may or may not be the right strategy, but minimally it seems it should check for presence or absence of 'www.' and not treat those as separate top level domains.
Proposed resolution
The logic of the cross-domain link check should be changed to pass href to isCrossDomain(); and, isCrossDomain() should be modified to match top-level domain fragments against the test hostname, rather than doing a literal string match.
Remaining tasks
Patch submitted for review.
| Comment | File | Size | Author |
|---|---|---|---|
| #11 | google_analytics-crossdomain-link-bug-1995922-11.patch | 729 bytes | timcosgrove |
| #1 | google_analytics-cross_domain_link_failure-1995922-1.patch | 2.01 KB | timcosgrove |
Comments
Comment #0.0
timcosgrove commentedcode snippet edit
Comment #0.1
timcosgrove commentedcode snippet edit
Comment #1
timcosgrove commentedPatch attached. I patched this against 7.x-1.x; the same fix probably applies to 7.x-2.x etc, but I am working with 7.x-1.3 and patched that.
Comment #2
hass commentedHave you really tested it? It works here. It only does not match wildcards or substrings. This is a known limitation, not a bug.
Comment #3
timcosgrove commentedHow would it work on an attribute 'hostname'? This doesn't exist.
Comment #4
hass commentedHave you tried it? This is jquery... It works.
Comment #5
timcosgrove commentedYes of course I tested it. I worked with this bug for several hours, thinking that there was a problem with my own Google Analytics setup, before I traced the bug to that line.
http://api.jquery.com/attr/
This function grabs attribute values off elements. In your case, if you had dynamically added a 'hostname' attribute to link or area elements that you were applying the callback to, this would work fine. However, nothing in your module does this, and nothing in Drupal core does this, to my knowledge.
I attempted to grab .attr('hostname') off an arbitrary link, for your benefit:
http://jsfiddle.net/VWTsv/1/
If you have test domains (two top-level domains) that are linked by cross-domain functionality that I can poke at, I would love to see them. Really, just here to help!
The other alternative is for me to get 2 spare domains, exposed to the internet and with nothing on them, to put clean Drupal installs + Google Analytics module on them to test this. But, since you claim it works, you no doubt have test domains that I can look at to see it working. It is very easy to confirm that cross-domain tracking is working; confirmation can be done client side.
Any help appreciated! Seriously, I would like to be wrong about the way you're using attr(), because if you are right I will have learned something completely new about jQuery, which I always welcome.
Comment #6
hass commentedhttp://stackoverflow.com/a/14613849
Place this code after
$(event.target).closest("a,area").each(function() {and you will see the hostname of the clicked link:Can you share a screenshot of List of top-level domains setting, please?
Comment #7
jec006 commentedHi,
This doesn't work - it is jquery and it is valid, however, hostname is not an attribute so jquery just returns empty string / undefined.
See http://jsfiddle.net/jec006/AR4cw/ for confirmation.
Comment #8
timcosgrove commentedAs requested: https://dl.dropboxusercontent.com/u/379975/ga_crossdomain_js_bug.mov
Really, just trying to help!
Comment #9
echeese commentedIt is a property of
<a>so if you change.attr('hostname')to.prop('hostname')it works for meComment #10
timcosgrove commentedHey! Apologies for the back and forth on this. I think I've found the problem, which is due to differences in jQuery.
http://api.jquery.com/attr/
Specifically:
$(this).attr('hostname') works in some cases because prior to jQuery 1.6, $.attr() would retrieve properties as well as actual attributes. Once you move to 1.6 or up, that is no longer the case.
If I run clean core Drupal, with your code, it works. If I install jQuery Update and run 1.5, it works. If I run 1.7, it fails.
You could argue that that is not your problem, and that's your prerogative; however, it'll be possible to keep the code compatible with old and new jQuery versions with a very small change.
I'll be back in a bit with a revised patch that only addresses this one issue.
Comment #11
timcosgrove commentedHi there-
As I said, I had hoped to learn something new about jQuery today, and I did: $.attr() would read non-attribute properties off elements prior to jQuery 1.6, which is unexpected and interesting.
I've changed the title of the issue to reflect the diagnosis, and attached a much less drastic patch which ensures that the cross-domain check will work for jQuery version both prior to 1.6 and from 1.6 on.
I do feel like the cross-domain handling could be done differently, but I'll submit that separately as a feature request.
Thanks!
Comment #12
timcosgrove commentedNote that this is because of the jQuery version. JSFiddle for better or for worse doesn't allow jQuery versions prior to 1.6.4, so it's difficult to confirm this there, but I was able to confirm that this sort of thing *would* work 1.5 or prior.
Comment #13
hass commentedI have tested it with chrome 4h ago.
Comment #14
hass commentedOh, what about prop() as attr() replacement?
Comment #15
timcosgrove commented$.prop() was added in 1.6. You would need to sniff for its existence or branch off jQuery version somehow, so that $.attr() was used when it was available and working for this particular case, and then replacing that somehow with $.prop() for versions when it becomes available.
Replacing `$(this).attr('hostname')` with `this.hostname` is both slightly more accurate and works both before and after the change. There's nothing to be gained by converting your element to a jQuery object just to get a property off it; standard Javascript does this just fine.
Comment #16
hass commentedSo we need a review if this safer than the current in all browsers down to IE6, FF3.5
I'm open to change the isCrossDomain function to substring match like google does, too. I just found it a bit difficult to implement.
Comment #17
hass commented#11: google_analytics-crossdomain-link-bug-1995922-11.patch queued for re-testing.
Comment #18
hass commentedCross post
Comment #19
hass commentedD7.1: http://drupalcode.org/project/google_analytics.git/commit/048072d
D7.2: http://drupalcode.org/project/google_analytics.git/commit/2146414
D8.2: http://drupalcode.org/project/google_analytics.git/commit/a60a5a6
Comment #20.0
(not verified) commentedtag abuse