Yes, I KNOW about Google Analytics. We use it for our overall site metrics, and I know we can track individual links. However, we needed a tracking solution for very specific links and we need that tracking data available to our web application in real time, so I wrote my own solution:
jQuery:
$.fn.track = function () {
var source, url, name, ref, $this;
$this = $(this);
if (window.location.search.substring(1) != '') {
source = window.location.pathname + "?" + window.location.search.substring(1);
} else {
source = window.location.pathname;
}
url = jQuery.URLEncode($this.attr('href'));
name = $this.attr('name');
ref = jQuery.URLEncode(source);
$this.live('click', function (click) {
click.preventDefault();
$.post('/lib/track.php', {
url: url,
name: name,
ref: ref
}, function () { window.location = $this.attr('href'); });
});
};
... using the jQuery URLEncode plugin (http://www.digitalbart.com/jquery-and-urlencode/).
Now, this code works fine with my PHP backend and on my machine, but it doesn't seem to work reliably for everyone else. Sometimes the parameters passed in via jQuery are NOT passed in, resulting in a record in the database with no name, url or ref.
For the life of me, I can't figure out why this might be happening; I know the $.post is triggering, since there are records in the database (in the PHP, I also record the IP of the request along with the timestamp), but in many cases the PHP script is receiving blank $_POST variables from jQuery.
I've tested it live on every browser I have access to at my workplace, and all of them work fine for me; however, about 75% of all the records created (not by my computers) come through as blank (most of them are using the same browsers I am).
Why could this be happening?
I think, in the end, my problem ended up being that it was taking too long for the request to be parsed by jQuery, and I'm pretty adamant about not wanting to make the links "dependent" on javascript (either that they wouldn't work without it or that the user would have to wait for the tracking request to complete before they hit the new page).
After browsing many other solutions online--borrowing from some and being inspired by others--I arrived at the solution below in native javascript:
if (document.getElementsByClassName === undefined) { // get elements by class name, adjusted for IE's incompetence
document.getElementsByClassName = function(className) {
var hasClassName, allElements, results, element;
hasClassName = new RegExp("(?:^|\\s)" + className + "(?:$|\\s)");
allElements = document.getElementsByTagName("*");
results = [];
for (var i = 0; (element = allElements[i]) !== null; i++) {
var elementClass = element.className;
if (elementClass && elementClass.indexOf(className) != -1 && hasClassName.test(elementClass)) {
results.push(element);
}
}
return results;
};
}
function addTracker(obj, type, fn) { // adds a tracker to the page, like $('xxx').event
if (obj.addEventListener) {
obj.addEventListener(type, fn, false);
} else if (obj.addEventListener) {
obj['e' + type + fn] = fn;
obj[type + fn] = function() {
obj['e' + type + fn]( window.event );
};
obj.attachEvent('on' + type, obj[type + fn]);
}
}
function save_click(passed_object) { // this function records a click
var now, then, path, encoded, to, from, name, img;
now = new Date();
path = '/lib/click.php';
from = (window.decode) ? window.decodeURI(document.URL) : document.URL;
to = (window.decodeURI) ? window.decodeURI(passed_object.href) : passed_object.href;
name = (passed_object.name && passed_object.name != '') ? passed_object.name : '[No Name]';
// timestamp the path!
path += '?timestamp=' + now.getTime();
path += '&to=' + escape(to) + '&from=' + escape(from) + '&name=' + name; // compile the path with the recorded information
img = new Image();
img.src = path; // when we call the image, we poll the php page; genius!
while (now.getTime() < then) {
now = new Date(); // resets the timer for subsequent clicks
}
}
function get_targeted_links(target) { // finds targeted elements and wires them up with an event handler
var links, link;
if (document.getElementsByClassName) {
links = document.getElementsByClassName(target);
for (var i = 0; i < links.length; i++) {
link = links[i];
if (link.href) {
addTracker(links[i], 'mousedown', save_click(links[i]));
}
}
}
}
addTracker(window, 'load', get_targeted_links('trackit'));
... which seems to be much snappier than the jQuery plugin I had written above, and so far has been fast enough to track all the requests I've thrown at it.
Hope that helps someone else!
These "clicks" might be coming from bots, or someone with JS disabled. If you the links clicked must be tracked why don't you consider JS only links, ie. put URL in a different attr other than href, then use your click handler to process it, add referral check in your track.php
Also have you checked if all elements are links?
Related
I am a newbie to jQuery / javascript and I had this working without checking the window size first. Then messed around with it and can not get it to work. The redirect is supposed to replace mysite.com/index.php?querystring with mysite.com/mobile.php?querystring if screen size is less then 699. Please help. Thank You.
This function seems to work exaclty how I need it but need to have onload with if screen size is less then.
$('a').each(function(index, a) {
var href = $(a).attr('href');
$(a).attr('href', 'http://mysite.com/mobile.php?redirect=' + href;)
}
}
//below is not working
function checkWidth() {
var windowSize = $(window).width();
if (windowSize <= 699) {
window.onload = function() {
/* onload code */
// Execute on load
//checkWidth();{
var anchors = document.getElementsByTagName("a");
for (var i = 0; i < anchors.length; i++) {
anchors[i].href = "http://mysite.com/mobile.php?redirect=" + anchors[i].href
/* function checkWidth() {
var windowSize = $(window).width();*/
}
}
If you intend on using jQuery, this should work:
$(document).ready(function() {
var window_width = $(window).width();
if( window_width < 699 ) {
$('a').each(function(index, a) {
var href = $(a).attr('href');
$(a).attr('href', 'http://mysite.com/mobile.php?redirect=' + href;
});
}
});
This is really something you should be doing server-side. Because someone isn't exactly going to be switching the device over the course of the session, you should check the device when they first visit the site, and then create a session variable storing it. Then, on every new page have the server check the variable and use it to determine which links to put in. If you're content with doing it client-side, though, Ryan Pilbeam's answer should work.
I have a script that pops up a div element when a anchor tag is clicked
echo '<h2>' . $row['placename'] . '</h2>';
It's echoed from a PHP script, and is working fine in FF and IE(9).
But chrome won't run it, atleast on ver. 18.0.1025.168 m
The popup(divid); event fires when I click it, but it doesent complete the function calling inside the script the function is in.
var width_ratio = 0.4; // If width of the element is 80%, this should be 0.2 so that the element can be centered
function toggle(div_id) {
var el = document.getElementById(div_id);
if ( el.style.display == 'none' ) { el.style.display = 'block';}
else {el.style.display = 'none';}
}
function blanket_size(popUpDivVar) {
alert(popUpDivVar);
if (typeof window.innerWidth != 'undefined') {
viewportheight = window.innerHeight;
} else {
viewportheight = document.documentElement.clientHeight;
}
if ((viewportheight > document.body.parentNode.scrollHeight) && (viewportheight > document.body.parentNode.clientHeight)) {
blanket_height = viewportheight;
} else {
if (document.body.parentNode.clientHeight > document.body.parentNode.scrollHeight) {
blanket_height = document.body.parentNode.clientHeight;
} else {
blanket_height = document.body.parentNode.scrollHeight;
}
}
var blanket = document.getElementById('blanket');
blanket.style.height = blanket_height + 'px';
var popUpDiv = document.getElementById(popUpDivVar);
popUpDiv_height=0;
popUpDiv.style.top = popUpDiv_height + 'px';
}
function window_pos(popUpDivVar) {
if (typeof window.innerWidth != 'undefined') {
viewportwidth = window.innerHeight;
} else {
viewportwidth = document.documentElement.clientHeight;
}
if ((viewportwidth > document.body.parentNode.scrollWidth) && (viewportwidth > document.body.parentNode.clientWidth)) {
window_width = viewportwidth;
} else {
if (document.body.parentNode.clientWidth > document.body.parentNode.scrollWidth) {
window_width = document.body.parentNode.clientWidth;
} else {
window_width = document.body.parentNode.scrollWidth;
}
}
var popUpDiv = document.getElementById(popUpDivVar);
window_width=window_width/2;
window_width = window_width * width_ratio;
popUpDiv.style.left = window_width + 'px';
}
function popup(windowname) {
alert(windowname); // THIS WORKS
blanket_size(windowname);
window_pos(windowname);
toggle('blanket');
toggle(windowname);
}
The last function in that script is the one that is called first. I put an alert box in it to verify that it was fired. BUT, I put an alert box in the next function that it calls (blanket_size), and it did not fire, as I had the alert box on the first line in the function. It did not fire.
I simply have no clue why. The weird thing is that this stuff works in other browsers, but not chrome. Any ideas?
Thanks!
Edit: And I also verified that the parameter value passed into the popup() function (the 'windowname' param) is valid/has a value. It contains the ID of a DIV that is in the HTML document, and it's not dynamically created.
Edit 2: Ok, I got the script running when and ONLY when I add an alert box with the parameter value in it (windowname) to the popup(windowname) function. But if I remove that box, it stops working again.
Edit 3:
Got no errors on the debugger at all. But now I'm even more confused. After a great deal of tries, it seems like it's working with the alert box at random! Sometimes works, and sometimes not.
Final Edit
Changed logic to jQuery. Should have done this long ago!
// Open property
$(".property-open-link", ".yme-propertyitem").live('click', function() {
$("#yme-property-pop").css({'display': 'block', 'z-index': '9999' });
$("#blanket").css({'display': 'block', 'height', getBlanketHeight(), 'z-index': '1000' });
loadproperties('open', $(this).closest(".yme-propertyitem").attr("id"));
});
// Close property button
$("#yme-property-close").live('click', function() {
$("#yme-property-pop").css('display', 'none');
$("#blanket").css('display', 'none');
});
Couple of things to clear up first:
It really helps if you create a way for us to interact with your
code, especially as you've pasted PHP code here, instead of plain
HTML
Is there a reason why you're not using a library to handle your DOM
interactions? It will make your code more concise and take away some
possible failure points when it comes to cross-browser code.
Right,
I'm a little unsure why your code isn't working in Chrome. I set up a demo in jsfiddle and it seems to work fine.
You'll notice I'm not attaching the events in onclick attributes on the <a/> element, and neither should you. This could be where the problem lies.
Currently, the code in the jsfiddle alerts as expected and only fails when it fails to find a relevent DOM node in toggle.
Note:
addEventListener in the example is not cross-browser, which is another reason to use a DOM library.
I have several divs that a user can Minimize or Expand using the jquery toggle mothod. However, when the page is refreshed the Divs go back to their default state. Is their a way to have browser remember the last state of the div?
For example, if I expand a div with an ID of "my_div", then click on something else on the page, then come back to the original page, I want "my_div" to remain expanded.
I was thinking it would be possible to use session variables for this, perhaps when the user clicks on the expand/minimize button a AJAX request can be sent and toggle a session variable...IDK..any ideas?
There's no need for an ajax request, just store the information in a cookie or in the localstorage.
Here's a library which should help you out: http://www.jstorage.info/
Some sample code (untested):
// stores the toggled position
$('#my_div').click(function() {
$('#my_div').toggle();
$.jStorage.set('my_div', $('#my_div:visible').length);
});
// on page load restores all elements to old position
$(function() {
var elems = $.jStorage.index();
for (var i = 0, l = elems.length; i < l; i++) {
$.jStorage.get(i) ? $('#' + i).show() : hide();
}
});
If you don't need to support old browsers, you can use html5 web storage.
You can do things like this (example taken from w3schools):
The following example counts the number of times a user has visited a
page, in the current session:
<script type="text/javascript">
if (sessionStorage.pagecount) {
sessionStorage.pagecount=Number(sessionStorage.pagecount) +1;
}
else {
sessionStorage.pagecount=1;
}
document.write("Visits "+sessionStorage.pagecount+" time(s) this session.");
</script>
Others have already given valid answers related to cookies and the local storage API, but based on your comment on the question, here's how you would attach a click event handler to a link:
$("#someLinkId").click(function() {
$.post("somewhere.php", function() {
//Done!
});
});
The event handler function will run whenever the element it is attached to is clicked. Inside the event handler, you can run whatever code you like. In this example, a POST request is fired to somewhere.php.
I had something like this and I used cookies based on which user logged in
if you want only the main div don't use the
$('#'+div_id).next().css('display','none');
use
$('#'+div_id).css('display','none');
*Here is the code *
//this is the div
<div id = "<?php echo $user; ?>1" onclick="setCookie(this.id)" ><div>My Content this will hide/show</div></div>
function setCookie(div_id)
{
var value = '';
var x = document.getElementById(div_id);
var x = $('#'+div_id).next().css('display');
if(x == 'none')
{
value = 'block';
}
else
{
value = 'none';
}
console.log(div_id+"="+value+"; expires=15/02/2012 00:00:00;path=/")
//alert(x);
document.cookie = div_id+"="+value+"; expires=15/02/2012 00:00:00;path=/";
}
function getCookie(div_id)
{
console.log( div_id );
var i,x,y,ARRcookies=document.cookie.split(";");
for (i=0;i<ARRcookies.length;i++)
{
x=ARRcookies[i].substr(0,ARRcookies[i].indexOf("="));
y=ARRcookies[i].substr(ARRcookies[i].indexOf("=")+1);
x=x.replace(/^\s+|\s+$/g,"");
if (x==div_id)
{
return unescape(y);
}
}
}
function set_status()
{
var div_id = '';
for(var i = 1; i <= 9 ; i++)
{
div_id = '<?php echo $user; ?>'+i;
if(getCookie(div_id) == 'none')
{
$('#'+div_id).next().css('display','none');
}
else if(getCookie(div_id) == 'block')
{
$('#'+div_id).next().slideDown();
}
}
}
$(document).ready(function(){
get_status();
});
Look about the JavaScript Cookie Method, you can save the current states of the divs, and restore it if the User comes back on the Site.
There is a nice jQuery Plugin for handling Cookies (http://plugins.jquery.com/project/Cookie)
Hope it helps
Ended up using this. Great Tutorial.
http://www.shopdev.co.uk/blog/cookies-with-jquery-designing-collapsible-layouts/
I am using AJAX extensively and my PHP based notification system was not sufficient.
I have this function:
<script>
function user_notify($string, $class){
if($class == null){
$class = 'error';
}
$('<div class="' + $class + '"><div class="notification-text">' + $string + '</div></div>').hide().appendTo('#system-notifications').fadeIn('slow');
}
function DeleteTask(SpanName, TaskId){
if (confirm("Are you sure you want to delete this task?")) {
var curDateTime = new Date(); //For IE
var status = document.getElementById('status');
var poststr = "uniqueID=" + curDateTime.getTime() ;
var SpanName = SpanName;
if(SpanName == 'project_todos_complete'){
var showCompleted = 1;
} else {
var showCompleted = 0;
}
//alert (SpanName);
makePOSTRequest('http://*****webdesigns.com/project_manager/include/ajax/global.php?action=delete_task&showCompleted=' + showCompleted + '&id=' + TaskId, poststr, SpanName);
}
if(ajax_status == 4){
user_notify('Task deleted.', 'success');
ajax_status = null;
}
}
</script>
I have a global javascript variable that holds the readyState. If 4 is a response from the server, we can assume the AJAX was successful (I know, not neccessarily the cgi/php is execute if any). So I store that, and within the function that called the AJAX post, if the readyState is 4, I call the user_notify function.
It works beautifully with one exception: the first action that should trigger a notification does not. All consecutive actions successfully generate a message. It's not a specific action that doesn't work, just the first one.
The html:
<body>
<div id="system-notifications"></div>
<div class="wrapper">...</div>
What am I missing here?
UPDATE:
I am in the process of moving legacy javascript/Ajax calls to jQuery/Ajax. Everything works except one aspect: the targeted div does not 'refresh' with the return data from the .ajax jQuery call. The notification pops up (the first time and all consecutive times), the php executes (refreshing the page verfies this), but the div does not update with the html that the PHP script generates.
$('form#addToDoForm').submit(function(){
var project_id = $('#addToDoForm input[name=project_id]');
var assigned_id = $('#addToDoForm input[name=assigned_user_id]');
var description = $('#addToDoForm textarea[name=description]');
var responsible_id = $('#addToDoForm input[name=responsible_user_id :selected]');
alert(responsible_id.val());
return false;
var due = $('#addToDoForm input[name=due]');
var result_div = 'project_todos_' + project_id;
var query_string = 'action=add_to_do&id=' + project_id;
var ajax_url = 'http://avwebdesigns.com/basecamp/include/ajax/global.php?' + query_string;
var successMessage = '<b>' + description.val() + '</b> added.';
var data =
'project_id=' + project_id.val() +
'&assigned_user_id=' + assigned_id.val() +
'&responsible_user_id=' + responsible_id.val() +
'&description=' + encodeURIComponent(description.val()) +
'&due=' + encodeURIComponent(due.val()); // encodeURIComponent()
$.ajax({
type: 'POST',
url: ajax_url,
data: data,
cache: false,
success: function(data){
$('#'+result_div).html(data); // $('#'+result_div).html(data.returnValue);
user_notify(successMessage, 'success');
},
error:function(data){
$('#'+result_div).html(data);
user_notify(failureMessage, 'error');
}
});
return false;
});
Any ideas?
You are missing the non-blocking characteristics of an AJAX call probably. I can't really tell what makePOSTRequest does, but judging from your design I assume you expect it to be synchronous where it probably is not. Due to the asynchronous nature of an AJAX call, you need to pass a callback function to the call that is called when the AJAX request completes.
What probably happens in your case is that makePOSTRequest immediately returns and because the first request hasn't finished yet, ajax_status will not be 4 yet. Then, by the time the second request is sent, your first will have completed and your global variable will have been set to 4, so this time it works and this is also the cause why it works in all subsequent attempts.
This shows another flaw in your design: it's actually a very bad idea to capture the status of an AJAX request in a global variable. These requests are potentially sent in a concurrent fashion so you would have several requests that share one and the same variable - this calls for a 'race condition'. Have a look at the examples in the Ajax section of the jQuery documentation to see how to handle this correctly with the help of a callback function.
Using a hashchange event I'm detecting when a user clicks the back button in a browser and changing the URL accordingly. Is there a better way to do this for pagination? I'm currently changing the URL after a user clicks my pagination control like so:
$(".pager").click(function(){
var start = null;
if ($.browser.msie) {
start = $(this).attr('href').slice($(this).attr('href').indexOf('#')+1);
}
else {
start = $(this).attr('href').substr(1);
}
$('#start').val(start);
$.post("visits_results.php", $("#profile_form_id").serialize(),
function(data) {
$('#search_results').html(data);
location.href = "#visits=" + start;
});
return false;
});
My javascript to detect the back button looks like this:
function myHashChangeCallback(hash) {
if (hash == "") {
$("#loadImage").show();
var no_cache = new Date().getTime();
$('#main').load("home.php?cache=" + no_cache, function () {
$("#loadImage").hide();
});
return false;
}
else {
// adding code to parse the hash URL and see what page I'm on...is there a better way?;
}
}
function hashCheck() {
var hash = window.location.hash;
if (hash != _hash) {
_hash = hash;
myHashChangeCallback(hash);
}
}
I currently plan on checking each hashtag and the value to see what page I should load unless there is a better more efficient way.
Any thoughts or suggestions?
The jQuery Address plugin does this very well. Once setup it provides a series of logical navigation events which you can hook into. It also has very good support for history.pushState() which eliminates the need for hashtags in newer browsers and has equally good fallback support for those browsers that do not support pushState.
A simple implementation would look like this:
// Run some code on initial load
$.address.init(function(e) {
// Address and path details can be found in the event object
console.log(e);
});
// Handle hashtag/pushState change events
$.address.change(function(e) {
// Do more fancy stuff. Don't forget about the event object.
console.log(e);
});
// Setup jQuery address on some elements
$('a').address();
To enable pushState() support pass an argument to the script like so:
<script type="text/javascript" src="jquery.address-1.3.min.js?state=/absolute/path/to/your/application"></script>