SiteCatalyst : Tracking Custom links on Webkit browsers - web-analytics

My query is that I have a link that redirects to another page. In webkit browsers, how to force the sitecatalyst server call (script execution) to finish before the redirect happens?
I am using sitecatalyst for a portal. I have
configured the custom link call to include the doneAction parameter for
successful call completion on webkit browsers (As mentioned in Adobe guide).
The custom link code for onClick event of button is as below:
<script language="javascript" >
function search(keyword)
{
var s=s_gi('testing');
s.linkTrackVars="prop11,events";
s.linkTrackEvents="event6";
s.prop11=keyword;
s.events="event6";
s.tl(this,'o','Search',navigate());
window.location=searchresults.html;
}
</script>
<script language="javascript" >
function navigate()
{
return false;
/*To induce a delay to ensure that image request is sent to Adobe before the
user leaves the page.(As given in Adobe guide - code release H.25))
Code version H.25 (released July 2012) includes an overloaded
track link method ( s.tl ) that forces WebKit
browsers to wait for the track link call to complete.)*/
}
</script>
However, even after this, I am getting error in custom link tracking. The redirect happens before the call can complete.
Please help on the same. Thanks in Advance.
Regards,
Harshil

Okay so firstly there are a number of issues with how you implemented it. Here is an example of how it should look like:
search
<script type='text/javascript'>
function search(keyword) {
var s=s_gi('testing');
s.linkTrackVars="prop11,events";
s.linkTrackEvents="event6";
s.prop11=keyword;
s.events="event6";
s.tl(this,'o','Search',null,navigate);
return false;
}
function navigate(){
window.location="searchresults.html";
}
</script>
Some points
You didn't actually post the link or whatever you are using that calls the search function so I have a link shown as an example.
You passed the navigate function as the 4th argument when it should be the 5th (with a null or blank string as a placeholder for the 4th)
It should be navigate not navigate(). The way you did it, you are making a call to the function and passing the result of the function as an argument. s.tl requires the actual function or reference to a function, and it will make the call to the function. In fairness, the Adobe document is typoed: it shows the example wrapped in quotes which doesn't work.
The redirect should be placed in navigate, not in search.

Replace the link href with javascript function
function trackLink(e) {
nextUrl = e.href;
e.href = "javascript:sendData('" + nextUrl + "')";
}
function sendData(url) {
s.tl(this, "o", "Link Name", null, function() {
window.location.href = url;
});
}
or try out the following
function sendData(obj) {
s.tl(obj, "o", "Link Name", null, "navigate");
return false;
}
Link

Link tracking is a dinosaur form of tracking as the numbers are barely accurate these days if you are not putting analytics before user experience. What I don't understand is that why don't you measure this on the next page instead of the link, unless you have no control over the next step?
As for your question: Previous examples on how to prevent link following before event is executed are quite solid, but remember if you have other JS code binded, be sure not to break it. As for the syntax, you can pass all variables to s.tl function as an object without setting linkTrackVars and linkTrackEvents for the s-object, which might have negative effects on events if case you use the code on dynamic pages.
E.g.
...
var data = {
linkTrackVars="prop11,events",
linkTrackEvents="event6",
prop11=keyword,
events="event6"
};
s.tl(this, "o", "Search", data, "navigate");
...
Note: You can't actually use props and events together in standard reporting. As per the code you pasted in comments to Crayon I can see that you are using eVars, so I assume that the example was not that accurate.

Related

jQuery Mobile 1.4+ pagecontainerbeforechange bug?

I am experimenting with the new way of handling page events in jqM and have run into a curious issue. When handling the pagecontainerbeforechange event
$(document).on('pagecontainerbeforechange',function(e,u){test(e,u,'changing');})
function test(e,u,msg){console.log($(u.toPage));}
Attempting to put a jQuery object wrapper around u.toPage - as done above - produces strange behavior.
Check out this fiddle to see what I mean
Click on the Second Page button and then view the console. Nothing will happen (the second page is not shown) and you will see a message along the lines of *Uncaught error:syntax error, unrecognized expression http://jsfiddle.net/egn7g5xb/1/show/#second
Now comment out Line 7 and run the fiddle again. No such issue this time and the second page gets shown.
Perhaps someone here might be able to explain what is going on here?
On initial run, jQuery Mobile creates a fake page before navigating to first page in DOM. At that stage, pagecontainerbeforechange fires twice and returns .toPage as an object.
Later on, upon navigating to other pages, it fires twice again; however, it returns a string first time (URL/hash) and second time it returns an object which is the page itself.
Therefore, when using that event, you have to determine whether .toPage is an object or a string.
$(document).on("pagecontainerbeforechange", function (e, data) {
if (typeof data.toPage == "string") {
/* parse url */
}
if (typeof data.toPage == "object") {
/* manipulate page navigating to */
}
});
Note that pagecontainerbeforetransition is similar to beforechange, however, it fires once and returns .toPage as an object.
First, create your pagecontainer events within $(document).on("pagecreate", "#first", function(){ .. }).
Then the selector for these events should be $(":mobile-pagecontainer") or $("body") NOT $(document).
function test(e,u,msg)
{
console.log(msg);
var IsJQ = u.toPage instanceof $;
console.log(IsJQ);
if (IsJQ){
console.log(u.toPage.data());
} else {
console.log(u.toPage);
}
console.log('---');
}
$(document).on("pagecreate", "#first", function(){
$(":mobile-pagecontainer").on('pagecontainerbeforechange', function (e, u) {
test(e,u,'changing');
});
$(":mobile-pagecontainer").on('pagecontainerchange',function(e,u){
test(e,u,'changed');
});
});
Updated FIDDLE

Bridge jQuery UI droppable to Lift ajax handler

I'm working on a web application project using Scala / Lift. I would like to make my application support drag and drop to improve the user experience.
But I'm not quite sure how to bridge jQuery part with Lift.
Currently I've a working UI part, an div block has class named listRow is dragable with an HTML5 attribute data-stuffid which has an ID from database.
It can be dragged to an block named nextAction, and I could print out the data-stuffid field correctly in JavaScript.
Here is my JavaScript code block:
<script type="text/javascript">
$('.listRow').draggable({
revert: true
});
$("#nextAction").droppable({
accept: ".listRow",
tolerance: "pointer",
activeClass: "dropActive",
hoverClass: "dropHover",
drop: function(event, ui) {
var stuffID = ui.draggable.data("stuffid")
console.log(stuffID)
}
})
But I don't know how to call to a Lift AJAX handler, and pass stuffID to Lift in the case.
What I would like to do is have an Scala function likes the following:
def showDialog(stuffID: String): JsCmds
{
// Do some server side work, ex: querying database
// Return a javascript cmds likes SHtml.ajaxButton...etc.
Alert("SutffID:" + stuffID)
}
And when user drag a .listRow to "#nextAction", showDialog would be called, and return the javascript to browser and let browser execute it.
You probably looking for one of these two functions. I'd take a look at :
JsCmds.Function and SHtml.ajaxCall
JsCmds.Function will allow you to create a JavaScript function, and SHtml.ajaxCall will allow you to execute code via ajax. You'd create a function in your Lift CSS Selector and then you'd be able to call that functions directly from your jQuery code.
There are a lot of discussions on using both those, some relevant info might be:
https://groups.google.com/forum/?fromgroups=#!topic/liftweb/4EyE1EbcDDM
https://groups.google.com/forum/?fromgroups=#!topic/liftweb/aAmvSjPxcyI
When initializing jquery from lift, just send lift ajax callback as parameter

TinyMCE not working in http request xhr ajax generated page

So i I have a page that contains links that call an httpRequest. The request calls a php file that grabs data from mysql and pre populates a form which is then returned to the browser/webpage. My problem is that when the page is returned to the browser via the httpRequest/ajax the text area does not display the tinymce editor, it just displays a normal text area. It looks like my request and ajax is working fine the text area just doesn't have the tinycme editor on it.
When i don't use ajax it works fine but when i put it in a separate file and call it via ajax it doesn't bring in the tinymce editor.
Does anyone know how to fix this problem so that my ajax generated page displays the text area with the tinymce editor. Thank you.
Lets presume that your thinyMCE instance is initialized with code below
// initialize tinyMCE in page
tinyMCE.init({
mode: "textareas",
theme: "advanced"
});
and you have some kind of button somewhere in the page. For purpose of this tip, i will not give it any ID but you may. Now, using jQuery you can easily attach event handler to that button which will call through AJAX your server and take content which you want to put tinyMCE editor. Code which will do such job would look somehow like below.
$(function() {
$("button").bind("click", function() {
var ed = tinyMCE.get('content');
ed.setProgressState(1); // Show progress
$.getJSON('/page/12.json', { /* your data */
}, function(data) {
ed.setProgressState(0); // Hide progress
ed.setContent(data["body"]);
}
});
});
});
You can see that on button.click ajax will call url /page/12.json which will return JSON as response. bare minimum of that response could be:
{
title: "Page title",
body: "<html><head><title>Page title</title>......</html>"
}
I attached anonymous function as callback which will handle response from server. and hide progress indicator which is shown before ajax call.
About JSON
JSON is shorten of JavaScript Object Notation. It is JavaScript code!!! So don't be confused about it. Using JSON you can make javascript object which can have attributes you can use later in your code to access particular peace of data which that object "holds". You can look at it as some kind of data structure if it is easier to you.
Anyway, to show you how this JSON can be created by hand look at examples below
var data = new Object();
data.title = "Page title";
data.body = "<html....";
or
var data = {
title: "page title",
body: "<html...."
};
it is very same thing.
If you want to learn more about JSON point your browser to http://json.org.
===== alternative =====
Alternative to json solution could be just plane ajax call to server and response can be plain HTML (from your question I can assume that you have something like this already). So instad of calling $.getJSON you can use $.get(url, callback); to do same thing. The code at the top of my answer will not dramatically change. Instead of geting JSON in response you will get string which is HTML.
----------- BOTTOM LINE -------
I prefer JSON since it can be easily extended later with other attributes, so there is no painful code changes later ;)
Problem here will be that when you return the full page and render it using the ajax response, your tinymce instance has not been shut down before.
In order to do this you can call this small piece of code before you render the ajax response:
tinymce.execCommand('mceRemoveControl',true,'editor_id');
In this case the editor should initialize correctly. You are not allowed to initialize a tinymce editor with the same id before shutting the first one down.
Strangely i ran into this problem yesterday. Following code should work, but YMMV. Trick is to use the correct steps in ajax events. I used the Regular TinyMCE and made use of the jQuery library already included.
Following goes into your tinyMCE initialization tinyMCE.init() . All of the below block should be outside the document.ready.
myTinyInit = {
//.......All essential keys/values ...........
setup : function(ed) {
ed.onChange.add(function( ed ) {
tinyMCE.triggerSave();
}) }
//.....................
};
// Init the tinyMCE
tinyMCE.init(myTinyInit);
This ensures the content is being saved regularly onto the textarea that holds the value. Next step is setting up the request events.
Normally tinyMCE mceAddControl before the ajax post and mceRemoveControl after the ajax success should do the trick. But I found that often does not work.
I used the form as the jQuery selector in my case.
jQuery( '.myForm' )
.find( 'textarea#myTextArea' )
.ajaxStart(function() {
// If you need to copy over the values, you can do it here.
// If you are using jQuery form plugin you can bind to form-pre-serialize event instead.
// jQuery( this ).val( tinyMCE.get( jQuery( this ).attr( 'id' )).getContent() );
}).ajaxSend( function() {
// ! - step 2
// My case was multiple editors.
myEds = tinyMCE.editors;
for( edd in myEds ) {
myEds[ eds ].remove();
}
// tinyMCE.get( 'myTextarea' ).remove();
// strangely mceRemoveControl didnt work for me.
// tinyMCE.execCommand( 'mceRemoveControl', false, jQuery( this ).attr('id'));
}).ajaxSuccess(function() {
// Now we got the form again, Let's put up tinyMCE again.
txtID = jQuery( this ).attr( 'id' );
// ! - step 3
tinyMCE.execCommand( 'mceAddControl', false, txtID );
// Restore the contents into TinyMCE.
tinyMCE.get( txtID ).setContent( jQuery( this ).val());
});
Problems i came across :
Using mceRemoveControl always gave me r is undefined error persistently.
If you get a blank tinyMCE editor, check the DOM whether the ID of the textarea is replaced with something like mce_02, this means that TinyMCE is being initialized again or something is wrong with the order. If so, the tinyMCE is duplicated with each save.
if you are new to JS, I recommend using jQuery with the form plugin, it might be easier for you. But do use the regular non-jquery tinyMCE, as it is well documented.
I fixed this problem by recalling the function after the ajax call. In this part of my ajax:
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById("Content").innerHTML=xmlhttp.responseText;
tinymce();
Now it works fine.

jQuery Functions need to run again after ajax is complete

I am developing a website that parses rss feeds and displays them based on category. You can view it here: http://vitaminjdesign.com/adrian
I am using tabs to display each category. The tabs use ajax to display a new set of feeds when they are clicked.
I am also using two other scripts- One called equalheights, which re-sizes all of the heights to that of the tallest item. And the other script I am using is called smart columns, which basically resize your columns so it always fills the screen.
The first problem I am having is when you click a new tab (to display feeds within that category). When a new tab is clicked, the console shows a jQuery error:
$(".block").equalHeights is not a function
[Break On This Error] $(".block").equalHeights();
The main problem is that each feed box fills up the entire screen's width (after you click on a tab), even if there are multiple feed boxes in that category.
MY GUESS - although all of the feeds (across all tabs) are loaded on pageload, when a new tab is selected, both jQuery scripts need to be run again. any ideas on how I can make this work properly?
One thing to note - I used the ajaxSuccess method for making equalHeights work on the first page...but it wont work after a tab is clicked.
My jQuery code for the tabs are below:
$(".tab_content").hide(); //Hide all content
$("ul.tabs li:first").addClass("active").show(); //Activate first tab
$(".tab_content:first").show(); //Show first tab content
$("#cities li:nth-child(1)").addClass('zebra');
$("#column li ul li:nth-child(6)").addClass('zebra1');
//On Click Event
$("ul.tabs li").click(function() {
$("ul.tabs li").removeClass("active"); //Remove any "active" class
$(this).addClass("active"); //Add "active" class to selected tab
$(".tab_content").hide(); //Hide all tab content
var activeTab = $(this).find("a").attr("href"); //Find the href attribute value to identify the active tab + content
$(activeTab).fadeIn(); //Fade in the active ID content
$(".block").equalHeights();
return false;
});
Thanks to Macy (see answer below), I have brought my jQuery script to the following: (still does not work)
$(document).ajaxSuccess(function(){
var script = document.createElement('script');
script.src = 'js/equalHeight.js';
document.body.appendChild(script);
equalHeight($(".block"));
I found some small problems in your code. I am not sure that my suggestions will solve all the problems, but I decide to describe my first results here.
1) You should remove comma before the '}'. Currently the call look like $("#column").sortable({/**/,});
2) The function equalHeight is not jQuery plugin. It is the reason why the call $(".block").equalHeights(); inside your 'click' event handler follows to the error "$(".block").equalHeights is not a function" which you described. You should change the place of the code to equalHeight($(".block")); like you use it on other places.
3) The script http://vitaminjdesign.com/adrian/js/equalHeight.js defines the function equalHeight only and not start any actions. Once be loaded it stay on the page. So you should not load it at the end of every ajax request. So I suggest to reduce the script
$(document).ajaxSuccess(function(){
var script = document.createElement('script');
script.src = 'http://vitaminjdesign.com/adrian/js/equalHeight.js';
document.body.appendChild(script);
equalHeight($(".block"));
$("a[href^='http:']:not([href*='" + window.location.host + "'])").each(function() {
$(this).attr("target", "_blank");
});
});
to
$(document).ajaxSuccess(function(){
equalHeight($(".block"));
$("a[href^='http:']:not([href*='" + window.location.host + "'])").each(function() {
$(this).attr("target", "_blank");
});
});
4) I suggest to change the code of http://vitaminjdesign.com/adrian/js/equalHeight.js from
function equalHeight(group) {
tallest = 0;
group.each(function() {
thisHeight = $(this).height();
if(thisHeight > tallest) {
tallest = thisHeight;
}
});
group.height(tallest);
}
to
function equalHeight(group) {
var tallest = 0;
group.each(function() {
var thisHeight = $(this).height();
if(thisHeight > tallest) {
tallest = thisHeight;
}
});
group.height(tallest);
}
to eliminate the usage of global variables tallest and thisHeight. I recommend you to use JSLint to verify all your JavaScript codes. I find it very helpful.
5) I recommend you to use any XHTML validator to find some small but sometime very important errors in the markup. Try this for example to see some errors. The more you follow the XHTML standards the more is the probability to have the same results of the page in different web browsers. By the way, you can dramatically reduce the number of the errors in your current code if the scripts included in the page will be in the following form
<script type="text/javascript">
//<![CDATA[
/* here is the JavaScript code */
//]]>
</script>
I didn't analysed the full code but I hope that my suggestions will solve at least some of problems which you described in your question.
Essentially, when you add a new element to the document, the equalheights script has not attached its behavior to that new element. So, the "quick fix", is probably to re-embed the equalheights script after an ajax request has completed so that it re-attaches itself to all elements on the page, including the elements you just added.
Before this line: $(".block").equalHeights(); , add a line of script which re-embeds/re-runs your equalheights script.
$.getScript('<the location of your equalHeightsScript>');
$.getScript('<the location of your smartColumnsScript>');
$(".block").equalHeights();
or
var script = document.createElement('script');
script.src = '<the location of your script>';
document.body.appendChild(script);
A better solution would be to upgrade the plugin so it takes advantage of live. However, I'm not up to that at the moment :)
Some Error Here
$("ul.tabs li").click(function() {
$("ul.tabs li").removeClass("active"); //Remove any "active" class
$(this).addClass("active"); //Add "active" class to selected tab
$(".tab_content").hide(); //Hide all tab content
.
.
.
});
Should be re-written like this
$("ul.tabs li").click(function() {
$(this).addClass("active").Siblings("li").removeClass("active");; //Remove any "active" class Add "active" class to selected tab
$(".tab_content").hide(); //Hide all tab content
.
.
.
});
I don't think you need to run the scripts again after the ajax, or at least that's not the "main" problem.
You seem to have some problems in the script smartColumn.js
Right now it seems to only operate on the ul with the id "column" ('#column'), and it is working on the one UL#column you do have, but of course your HTML has many other "columns" all of which have the class "column" ('.column') that you want it to work on as well.
Just to get the beginning of what you are trying to do, change all the selectors in smartColumn.js that say 'ul#column' to say 'ul.column' instead, and then alter the HTML so that the first "column" has a class="column" rather than an id="column".
That should solve the 100% wide columns at least.
That should solve your "Main" Problem. But there are other problems.

onclick not firing for download

Im having trouble with a onclick event for a download. It does not want to fire when I click on the link. It just carries on as normal. But it does work with an external link. Have I done something obviously wrong?
Royal Society of Wildlife Trusts.
2010-2015 strategy,
Its doing the same in FF, Chrome and IE 8.
function trackExternalLink(link, action) {
_gaq.push(['_trackPageview', action]);
setTimeout('document.location = "' + link.href + '"', 25000)
return false;
}
You're not returning false from your onclick handler. You're returning false from the function it calls, but then throwing that away in the handler. You meant:
onclick="return trackExternalLink(this, '/links-the_royal_society_of_wildlife_trusts');"
However all this onclick stuff is pretty ugly. Consider unobtrusive scripting. And lose the nasty call to setTimeout creating script from a string. Do Google really recommend this code? It kinda sucks.
Here's a quick hack version that wraps the action into a class name:
Royal Society of Wildlife Trusts.
<script type="text/javascript">
for (var i= document.links.length; i-->0;)
if (document.links[i].className.substring(0, 5)==='ping-')
document.links[i].onclick= pingclick;
function pingclick() {
var action= this.className.substring(5);
var href= this.href;
_gaq.push(['_trackPageview', action]);
setTimeout(function() {
location.href= href;
}, 25000);
return false;
}
</script>
This still isn't great. 25 seconds (25000) is obviously way too long a delay; you probably want to pull that down to around 200ish. Really it should be following the link as soon as the XMLHttpRequest that passes the information to the server is complete, rather than using an arbitrary delay, but Google don't seem to give you that option. How poor. Then again, link-tracking pingback scripts are a nasty, user-hostile activity anyway.
I believe you need to return false to prevent the click from functioning as normal, this may, however, be jQuery specific.
I would also recommend using a framework rather than onclick attributes, they're messy and the framework will also be helpful in other respects.

Resources