I've been all through this site and many others looking for performance tips on JQuery, JQuery Mobile, and Phonegap. I have 8 data-role="page" pages in 1 HTML file and another page in a separate HTML file that loads the Facebook for Phonegap Build plugin files. I'm on JQuery 1.9.1, JQuery Mobile 1.3.0, and Phonegap Build on a Trio Stealth Pro 9.7 tablet w/ ICS and an LG Optimus V phone w/ Froyo. I've found many tips, but after my UX review my app was deemed unacceptable on Android due to unresponsiveness of page loads and button pushes.
I have to agree. The first time through the app, each page load take around 3700ms according to simple console instrumentation. This goes down to around 1900ms the second time to a page, which is OK by me. However, this resets back to the 3700ms every time the app is reloaded. I only wish whatever DOM indexing/searching / JS compiling that was being done each time could be cached or I could interrrupt all the JQuery backend processes because I don't use much of it outside of the UI components. I have disabled AJAX navigation. I've tried disabling page transitions but have found $.mobile.transitionFallbacks.* very helpful and I don't see any benefit from totally disabling transitions. I only use events as an attempt to explicitly turn on and off the Loading icon. I really don't use anything else JQuery Mobile. My DOM size is 1125 objects, though.
If I can't resolve these page loads I'm going to have to go native for both Android and iOS, and I'm not looking forward to the Objective C...
Here's what I've done so far:
Page Header load:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<title>Title</title>
<link href="jquery-mobile/mine.css" rel="stylesheet" type="text/css"/>
<link href="jquery-mobile/jquery.mobile.structure-1.3.0.min.css" rel="stylesheet" type="text/css"/>
<script src='jquery-mobile/fastclick.js' type='application/javascript'></script>
<script src="jquery-mobile/jquery-1.9.1.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).bind("mobileinit", function(){
$.mobile.ajaxEnabled = false;
$.mobile.pushStateEnabled = false;
$.mobile.touchOverflowEnabled = false;
$.mobile.defaultPageTransition = 'slide';
$.mobile.defaultDialogTransition = 'pop';
$.mobile.transitionFallbacks.slide = 'none';
$.mobile.transitionFallbacks.pop = 'none';
$.mobile.buttonMarkup.hoverDelay = 0;
$.mobile.phonegapNavigationEnabled = true;
});
</script>
<script src="jquery-mobile/jquery.mobile-1.3.0.min.js" type="text/javascript"></script>
<script src="jquery-mobile/mine.js" type="text/javascript"></script>
<script src="phonegap.js"></script>
I have removed all onClick's in my HTML.
I have cached all JQuery calls.
var $activityCheckboxes = $('#activityCheckboxes');
I limit JQuery calls to only reference by ID.
I have limited all calls to localStorage.
I have limited all String operations the best I could, e.g. concat, etc. I still am using += instead of push an array full and joining it because data I found suggested the += was actually faster.
I have minify-ed my CSS and Javascript. I'm about to try HeadJS lazy load (parallel load).
I'm using FastClick.
Like I said above, I've tried disabling all page transitions.
I close all events with event.preventDefault(); return false; except pagecreate's.
Here are my events:
$(document).delegate("#page", "pagecreate", function () {
validateValues();
validateActivitiesInstructions();
validateAddNewInstructions();
updateSettings();
});
$(document).delegate("#page", "pageshow", function () {
$.mobile.loading('hide');
event.preventDefault();
return false;
});
$(document).delegate("#about", "pageshow", function () {
$.mobile.loading('hide');
event.preventDefault();
return false;
});
$(document).delegate("#explanation", "pageshow", function () {
$.mobile.loading('hide');
event.preventDefault();
return false;
});
$(document).delegate("#settings", "pageshow", function () {
$.mobile.loading('hide');
event.preventDefault();
return false;
});
window.addEventListener('load', function () {
new FastClick(document.body);
event.preventDefault();
return false;
}, false);
What else can I do to decrease the initial page load time and possibly increase button responsiveness? Should I split my 8 pages into separate HTML files? Any help would be greatly appreciated! Thanks!
OK, I've experimented with every permutation I could think of and what I had above and the use of head.js seems to be the absolute best I'm going to do today. Thanks everyone for their help.
You may want to consider placing all of your JS assests at the bottom of the page. The problem caused by scripts is that they block parallel downloads. The HTTP/1.1 specification suggests that browsers download no more than two components in parallel per hostname. If you serve your images from multiple hostnames, you can get more than two downloads to occur in parallel.
While a script is downloading, however, the browser won't start any other downloads, even on different hostnames.
You may also want to concatenate some of your files together and minify the resulting file. Less HTTP requests can sometimes save you some loading time. But be careful to find a good balace between http requests and file size. If the global file becomes to large it will in turn, itself become a blocker, especially if you are firing a ton of events on dom ready.
Another thing to try would be experimenting with jQuery Mobile "Custom Download builder". Get rid of the features you don't need.
Related
I am building an app with extensive audio requirements, so I am using SoundJS for that, and I am compiling the app using phonegap.
I am using the mobile safe approach to build a soundJS app. It seems that there are different audio plugins, including a special Cordova audio plugin. So, I am not able to register any of these plugins on the compiled app. This is because registering any plugin requires to check if this plugin is supported or not. In case of the cordova, the method isSupported checks for the following:
if (s._capabilities != null || !(window.cordova || window.PhoneGap || window.phonegap) || !window.Media) { return;}
This means when the app is compiled, there is no global variable called window.cordova or phonegap and no global variable called window.media (I think this is the media plugin that needs to be installed to get soundjs to work, and I have added it to the config.xml that I'm using for phonegap build.
So the question is, how to investigate what is wrong, how to know if for example the media plugin is not installed properly (all from the javascript variables that we can use, as I am not able to use any other debugging), or is it the case that when I compile using phonegap build there is no variables for cordova or phonegap.. can we list all global variables to see which ones are used?
Edit
Thanks Jesse for drawing my attention to these points about phonegap, so I built a small app just to test the deviceready event, but for some reason it still doesn't work when compiled by phonegap build:
<!DOCTYPE html>
<html>
<head>
<title>Cordova Device Ready Example</title>
<script type="text/javascript" charset="utf-8" src="js/soundjs-NEXT.min.js"></script>
<script type="text/javascript" charset="utf-8" src="js/cordovaaudioplugin-NEXT.min.js"></script>
<script type="text/javascript" charset="utf-8">
// Call onDeviceReady when Cordova is loaded.
//
// At this point, the document has loaded but cordova-2.3.0.js has not.
// When Cordova is loaded and talking with the native device,
// it will call the event `deviceready`.
//
function onLoad() {
document.getElementById("doc_loaded").innerHTML="Document Loaded"
document.addEventListener("deviceready", onDeviceReady, false);
}
// Cordova is loaded and it is now safe to make calls Cordova methods
//
function onDeviceReady() {
// Now safe to use the Cordova API\
document.getElementById("device_loaded").innerHTML="Device Loaded"
if (window.cordova || window.PhoneGap || window.phonegap){
document.getElementById("phonegap_loaded").innerHTML="Phonegap Loaded"
}
if (window.Media){
document.getElementById("media_loaded").innerHTML="Media Loaded"
}
}
</script>
</head>
<body onload="onLoad()">
Hello Hello, testing phonegap deviceready
<div id="doc_loaded">Loading Doc</div>
<div id="device_loaded">Loading Device</div>
<div id="phonegap_loaded">Detecting Phonegap</div>
<div id="media_loaded">Detecting Media</div>
</body>
</html>
Can you please help me locate where can the problem be?
EDIT2
I figured out that the deviceready was not working because I didn't add cordova:
<script type="text/javascript" src="cordova.js"></script>
So, when I did, I was able to initialize the cordova audio plugin. However, I am still unable to play sound, despite using mobile safe approach:
(this code is hosted on arbsq.net/h6/)
<!DOCTYPE html>
<html>
<head>
<title>SoundJS: Mobile Safe</title>
<link href="css/shared.css" rel="stylesheet" type="text/css"/>
<link href="css/examples.css" rel="stylesheet" type="text/css"/>
<link href="css/soundjs.css" rel="stylesheet" type="text/css"/>
<script src="js/examples.js"></script>
</head>
<body onload="loading_doc()">
<header class="SoundJS">
<h1>Mobile Safe Play</h1>
<p>This example registers and plays a sound with SoundJS in a way that will
work on mobile devices.</p>
</header>
<div class="content" id="content" style="height: auto">
<p id="status">Hello World.</p>
</div>
<div id="error">
<h2>Sorry!</h2>
<p>SoundJS is not currently supported in your browser.</p>
<p>Please log a bug
with the device and browser you are using. Thank you.</p>
</div>
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" charset="utf-8" src="js/soundjs-NEXT.min.js"></script>
<script type="text/javascript" charset="utf-8" src="js/cordovaaudioplugin-NEXT.min.js"></script>
<!-- We also provide hosted minified versions of all CreateJS libraries.
http://code.createjs.com -->
<script id="editable">
var displayMessage; // the HTML element we use to display messages to the user
this.myNameSpace = this.myNameSpace || {};
function loading_doc() {
if(( /(ipad|iphone|ipod|android|windows phone)/i.test(navigator.userAgent) )) {
document.addEventListener('deviceready', init, false);
} else {
init();
}
}
function init() {
// store this off because walking the DOM to get the reference is expensive
displayMessage = document.getElementById("status");
// if this is on mobile, sounds need to be played inside of a touch event
if (createjs.BrowserDetect.isIOS || createjs.BrowserDetect.isAndroid || createjs.BrowserDetect.isBlackberry || createjs.BrowserDetect.isWindowPhone) {
//document.addEventListener("click", handleTouch, false); // works on Android, does not work on iOS
displayMessage.addEventListener("click", handleTouch, false); // works on Android and iPad
displayMessage.innerHTML = "Touch to Start";
}
else {
handleTouch(null);
}
}
// launch the app inside of this scope
function handleTouch(event) {
displayMessage.removeEventListener("click", handleTouch, false);
// launch the app by creating it
var thisApp = new myNameSpace.MyApp();
}
// create a namespace for the application
// this is a function closure
(function () {
// the application
function MyApp() {
this.init();
}
MyApp.prototype = {
src: null, // the audio src we are trying to play
soundInstance: null, // the soundInstance returned by Sound when we create or play a src
displayStatus: null, // the HTML element we use to display messages to the user
loadProxy: null,
init: function () {
// store the DOM element so we do repeatedly pay the cost to look it up
this.displayStatus = document.getElementById("status");
// this does two things, it initializes the default plugins, and if that fails the if statement triggers and we display an error
// NOTE that WebAudioPlugin plays an empty sound when initialized, which activates web audio on iOS if played inside of a function with a touch event in its callstack
if (!createjs.Sound.initializeDefaultPlugins()) {
document.getElementById("error").style.display = "block";
document.getElementById("content").style.display = "none";
return;
}
// Create a single item to load.
var assetsPath = "audio/";
this.src = assetsPath + "M-GameBG.ogg";
this.displayStatus.innerHTML = "Waiting for load to complete."; // let the user know what's happening
// NOTE createjs.proxy is used to apply scope so we stay within the touch scope, allowing sound to play on mobile devices
this.loadProxy = createjs.proxy(this.handleLoad, this);
createjs.Sound.alternateExtensions = ["mp3"]; // add other extensions to try loading if the src file extension is not supported
createjs.Sound.addEventListener("fileload", this.loadProxy); // add event listener for when load is completed.
createjs.Sound.registerSound(this.src); // register sound, which preloads by default
return this;
},
// play a sound inside
handleLoad: function (event) {
this.soundInstance = createjs.Sound.play(event.src); // start playback and store the soundInstance we are currently playing
this.displayStatus.innerHTML = "Playing source: " + event.src; // let the user know what we are playing
createjs.Sound.removeEventListener("fileload", this.loadProxy); // we only load 1 sound, so remove the listener
}
}
// add MyApp to myNameSpace
myNameSpace.MyApp = MyApp;
}());
</script>
</body>
</html>
#hmghaly,
the general method for checking for the availability of Phonegap is to use the 'deviceready' event that Cordova/Phonegap provide. In addition, it is required that you wait until this event completes.
You will want to read #4 of this article FAQ:
Top Mistakes by Developers new to Cordova/Phonegap
I will quote the important part from the documentation (which your should read):
This is a very important event that every Cordova application should use.
Cordova consists of two code bases: native and JavaScript. While the native code is loading, a custom loading image is displayed.
However, JavaScript is only loaded once the DOM loads. This means your
web application could, potentially, call a Cordova JavaScript function
before it is loaded.
The Cordova deviceready event fires once Cordova has fully loaded. After the device has fired, you can safely make calls to Cordova function.
The documentation includes code examples that would relevant to your particular mobile device and platform.
Best of Luck
Whilst it is not a complete answer I am currently working through the exact same problem and it was breaking at the exact same point.
if (s._capabilities != null || !(window.cordova || window.PhoneGap ||
window.phonegap) || !window.Media) { return;}
After you have ensured cordova is installed the next big thing is to ensure you actually have the cordova-plugin-media installed. The !window.Media bit in the line above. Sounds easy but if you simply add the plugin and build without reading all the output you can come unstuck.
The media plugin requires cordova version > 5.0 . The problem is that cordova is pinned at version 4.1.1 - at least mine was despite repeated total removal of cordova - several times via npm and manual total deletion of all npm caches.
Cordova is hard wired internally to install a particular version unless you tell it not to.
So make sure you are using
cordova platform add android#5.X.X
as appropriate to your version not just a plain old
cordova platform add android (BAD)
which will install the pinned version
If you do the latter cordova will happily build with version 4.1.1 despite the cli command
cordova -v
reporting you are on later version - in my case 5.4.1
It will then hit the plugin step - decide the environment is not appropriate for your plugin - spits out a warning and merrily continues with the build - minus the media plugin. Everything else will seem to work - the app will run and unless you dig into it you won't notice you are on an old version of cordova.
Note: they have just released a new version which moves the pinned version forward - so if you update to the latest version - you should be fine.
New Cordova Version Released
If you are using SoundJS 0.6.2, then you don't have to include the MobileSafe code. Refer Official Doc
The problem I was facing from quite a long time was the local sound files were not loading successfully in iOS.
What I found:
Latest iOS uses WKWebView. It appears to treat local files as if they came from a remote server, even though they're in the app itself, and such requests are blocked. Reference Source
Finally after lot of debugging and logging,
the following solution worked for me:
Add the Corodova file plugin.
cordova plugin add cordova-plugin-file
Change the local file path to this:
cdvfile://localhost/bundle/www/you_folder_name/file_name.mp3
I am relatively new to web-programming and am looking for a simple pattern to show a "loading"/waiting view for the web. Before I say anything else, I am sending only the minimum amount of data from the server, and start sending the JS/HTML resources to client while the client is using AJAX to request more data from the server (this might be suboptimal but bear with me). So basically, in theory this should mean the web view pops up earlier initially, but spends more time loading some of the data and corresponding subviews. Thus the need for a loading view.
So we have the standard jQuery function .ready()
fetchSomeDataAsynchronously(); //self-explanatory
$(document).ready(function () {
//should I load waiting view here or can I initialize it earlier??
window.mainUserHomeView = new MainUserHomeView({el: $("#user-home-main-div")});
window.mainUserHomeView.render();
window.userHomeMainTableView = new UserHomeMainTableView({el: $("#user-home-main-table-div")});
window.userHomeMainTableView.render();
fetchTeamSnapTeams(); //fetch more data asynchronously
});
Maybe my question is simply - can I show a loading screen before .ready() fires and what does that look like?
Yes you can. Just include the script to show the view in your HTML's head or at the beginning of the body. Make sure that you put it after you've loaded your required resources (e.g. jQuery) though.
Simplified example:
<html>
<head>
<script type="text/javascript">
function showLoadingView() {
// ...
}
showLoadingView();
</script>
</head>
<body>
<!-- ... -->
</body>
</html>
I'm creating a site which uses Cufon and is particularly heavy in terms of page-weight due to a large amount of Javascript. Therefore I'm trying to load in the script asynchronously with head.js ( http://headjs.com/ ) like so:
head.js("http://ajax.microsoft.com/ajax/jquery/jquery-1.4.2.min.js", function() {
head.js("/js/libs/cufon-yui.js", function() {
head.js("/js/shared/Stag_Bold_700.font.js" , function() {
Cufon.replace('h1', { fontFamily: 'Stag Bold' });
});
});
});
So Jquery is downloaded first, the subsequent cufon lib file and cufon font are downloaded in sequence and then Cufon is finally called to replace the H1. Obviously, this is a trimmed down example with fewer replacements but this still doesn't work when just attempting to replace the H1.
The problem is that ONLY in Internet Explorer (6/7/8), the text is not replaced but I can see that Cufon has definitely been called. I can ascertain this because the tag has the class "cufon-active cufon-ready" added to it. When I inspect the markup using the IE Developer toolbar, the cufon/cufoncanvas tags are there inside the selected elements but are, for want of a better word, invisible.
In IE9, the script behaves as intended similar to Chrome and Firefox. I have tried adjusting the Cufon drawing engine and have updated to the latest 1.09i version for good measure. If I move the Cufon calling statements to the document ready event instead of loading asynchronously, it works but I am trying to optimize page load and my site will be using a number of Cufon fonts as well as many other JS plug-ins. I've also tried using both labs.js and head.js to load the appropriate files asynchronously.
I had the same problem - I addressed this by using the browser detection of head.js to do the following:
if (head.browser.mozilla || head.browser.webkit || head.browser.opera ||
(head.browser.ie && (head.browser.version == '9.0'))) {
head.js('script/jquery.min.js',
'script/cufon.js', function () {
head.js('script/bebas_neue_400.font.js', function () {
Cufon.replace('h1', {
textShadow: '1px 1px rgba(0, 0, 0, 0.2)'
}).now();
// or a head.js('scripts/file.with.cufon.replacement.js');
});
});
} else {
// here we load scripts depending on GZIP support for this browser
document.write('\x3Cscript type="text/javascript" src="script/jquery.min.js">\x3C/script>');
document.write('\x3Cscript type="text/javascript" src="script/cufon.js">\x3C/script>');
document.write('\x3Cscript type="text/javascript" src="script/bebas_neue_400.font.js">\x3C/script>');
document.write('\x3Cscript type="text/javascript" src="script/file.with.cufon.replacement.js">\x3C/script>');
}
You could also use conditional comments (I didn't because I am also doing GZIP support detection in JavaScript and needed to adjust the scripts which are loaded dynamically.)
It's a hack, but should be useful enough until it's addressed within the library itself.
(I have also posted GIST with a more complete example here)
try calling
<script type="text/javascript"> Cufon.now(); </script>
just before </body> tag closes.
Try adding Cufon.now() after the Cufon.replace call, like so:
Cufon.replace('h1', { fontFamily: 'Stag Bold' });
Cufon.now();
I solved this in a way similar to CameraSchoolDropout's approach, except instead of using Document.write, I'm using IE conditional tags, and YepNope.js.
This issue on github says that they had problems using document.createElement('script'), and I just felt better using IE conditionals.
You can see an example page that I created at http://epraxadev.com/yepnope/
<head>
<style type="text/css">
#txt { visibility:hidden; }
</style>
<!--[if lte IE 8]>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
<script src="js/cufon-yui.js"></script>
<script src="js/adventor.cufon.js"></script>
<![endif]-->
<script src="js/modernizr.custom.js"></script>
<script>
yepnope([{
test: window.jQuery,
nope: {
'yJ': '//ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js',
'yCufon': 'js/cufon-yui.js',
'yFont': 'js/museo.font.js'
},
callback: {
'yJ': function(){
console.log("YepNope loaded jQuery! This isn't IE!");
}
},
complete: function() {
console.log('All browsers, including IE, will show this');
Cufon.replace('h1');
$(document).ready(function(){
$('#txt').css('visibility', 'visible');
});
}
}]);
</script>
<noscript>
<style type="text/css">
#txt { visibility:visible; }
</style>
</noscript>
</head>
For now just load jQuery and Cufón using regular <script> tags and load the subsequent files using a script loader.
Using document.write is a bad approach since it will only work if the script is loaded/executed before DOMReady and using browser sniffing to do it isn't a good approach either since it can give false results.
Conditional comments aren't a good solution either because you may need to update the scripts in the future and you will have to remember to update it in 2 different places which is bad for maintainability.
Follow this issue on GitHub to know when the bug is fixed.
I've just stared using JQuery and SoundManger2, and I have noticed that SoundManager has problems in certain situations where JQuery is used. And it also depends if Firefox (3.6.13) or IE(8.0.7600) is being used.
All I'm trying to test is weather I can play a sound or not. In each example I will show the code first and after the code I will identify if IE and FireFox succeeded or failed.
<html>
<head>
<title></title>
<script type="text/javascript" src="/Project/PublicWebSite/Scripts/soundmanager2.js"></script>
<script type="text/javascript" src="/Project/PublicWebSite/Scripts/jquery-1.5.js"></script>
<script type="text/javascript">
soundManager.debugMode = true;
soundManager.defaultOptions.volume = 50
soundManager.debugFlash = true; // enable flash debug output for this page
soundManager.url = '/Project/PublicWebSite/Scripts/swf/soundmanager2.swf';
soundManager.flashVersion = 8; // optional: shiny features (default = 8)
soundManager.useFlashBlock = false; // optionally, enable when you're ready to dive in
//enable HTML5 audio support, if you're feeling adventurous. iPad/iPhone will always get this.
//soundManager.useHTML5Audio = true;
soundManager.onready(function () {
soundManager.createSound('helloWorld', '/Project/PublicWebSite/Content/Sounds/Chime.mp3');
soundManager.play('helloWorld');
});
</script>
</head>
<body>
</body>
</html>
IE: Success; FireFox: Success
In following code everything is the same except I add the configuration for SoundManager in JQuery document load.
<html>
<head>
<title></title>
<script type="text/javascript" src="/Project/PublicWebSite/Scripts/soundmanager2.js"></script>
<script type="text/javascript" src="/Project/PublicWebSite/Scripts/jquery-1.5.js"></script>
<script type="text/javascript">
$(function () {
soundManager.debugMode = true;
soundManager.defaultOptions.volume = 50
soundManager.debugFlash = true; // enable flash debug output for this page
soundManager.url = '/Project/PublicWebSite/Scripts/swf/soundmanager2.swf';
soundManager.flashVersion = 8; // optional: shiny features (default = 8)
soundManager.useFlashBlock = false; // optionally, enable when you're ready to dive in
//enable HTML5 audio support, if you're feeling adventurous. iPad/iPhone will always get this.
//soundManager.useHTML5Audio = true;
soundManager.onready(function () {
soundManager.createSound('helloWorld', '/Project/PublicWebSite/Content/Sounds/Chime.mp3');
soundManager.play('helloWorld');
});
});
</script>
</head>
<body>
</body>
</html>
IE: Success; FireFox: Fail
Changed the order of JQuery and SoundManger script references
<html>
<head>
<title></title>
<script type="text/javascript" src="/Project/PublicWebSite/Scripts/jquery-1.5.js"></script>
<script type="text/javascript" src="/Project/PublicWebSite/Scripts/soundmanager2.js"></script>
<script type="text/javascript">
$(function () {
soundManager.debugMode = true;
soundManager.defaultOptions.volume = 50
soundManager.debugFlash = true; // enable flash debug output for this page
soundManager.url = '/Project/PublicWebSite/Scripts/swf/soundmanager2.swf';
soundManager.flashVersion = 8; // optional: shiny features (default = 8)
soundManager.useFlashBlock = false; // optionally, enable when you're ready to dive in
//enable HTML5 audio support, if you're feeling adventurous. iPad/iPhone will always get this.
//soundManager.useHTML5Audio = true;
soundManager.onready(function () {
soundManager.createSound('helloWorld', '/Project/PublicWebSite/Content/Sounds/Chime.mp3');
soundManager.play('helloWorld');
});
});
</script>
</head>
<body>
</body>
</html>
IE: Fail; FireFox: Success
If all I was doing was making static web pages this wouldn't be a problem. I'm creating code in asp.net MVC using layers and such and the order of how things get loaded is imporant. This is how I orginally stumbled upon this problem.
Since I'm a noob to JQuery and SoundManger, there is a very good possibility that I'm doing something wrong. If anyone has any comments of how to do this better, I would appricate any answers. I banged my head against my keyboard for a good while, before I figured this out and hope this will help someone else.
Update
When sound doesn't play I get the following info from SoundManager2
-- SoundManager 2 failed to load (security/load error) --
soundManager.disable(): Shutting down
soundManager: Failed to initialise.
soundManager: Verify that /Project/PublicWebSite/Scripts/swf/soundmanager2.swf is a valid path.
soundManager: No Flash response within expected time. Likely causes: Loading soundmanager2_debug.swf may have failed (and/or Flash 8+ not present?), Flash blocked or JS-Flash security error. See SWF output for more debug info.
soundManager: Getting impatient, still waiting for Flash...
soundManager::initMovie(): Waiting for ExternalInterface call from Flash..
soundManager::initMovie(): Got EMBED element (created via JS)
soundManager::createMovie(): Trying to load ./soundmanager2_debug.swf
-- SoundManager 2 V2.97a.20110123 (AS2/Flash 8), normal polling --
I noticed in Firebug and Fiddler that when I get this error, SoundManger tries to find
soundmanager2_debug.swf # /project/PublicWebSite/static/. The problem is that my swf files are not located there. This is where my HTML file is located.
Update
Simon, that pointed me in the correct direction. I didn't have to change anything in the soundmanger2.js file as mentioned at http://www.schillmania.com/projects/soundmanager2/doc/getstarted/#lazy-loading .
I just remove the refernce to the SoundManger script and dynamicly loaded the script using JQuery ajax call.
<html>
<head>
<title></title>
<script type="text/javascript" src="/Project/PublicWebSite/Scripts/jquery-1.5.js"></script>
<script type="text/javascript">
$(function () {
$.ajax({
url: '/Project/PublicWebSite/Scripts/soundmanager2.js',
dataType: 'script',
success:
{
soundManager.debugMode = true;
soundManager.defaultOptions.volume = 50
soundManager.debugFlash = true; // enable flash debug output for this page
soundManager.url = '/Project/PublicWebSite/Scripts/swf/soundmanager2.swf';
soundManager.flashVersion = 8; // optional: shiny features (default = 8)
soundManager.useFlashBlock = false; // optionally, enable when you're ready to dive in
//enable HTML5 audio support, if you're feeling adventurous. iPad/iPhone will always get this.
//soundManager.useHTML5Audio = true;
soundManager.onready(function () {
soundManager.createSound('helloWorld', '/Project/PublicWebSite/Content/Sounds/Chime.mp3');
soundManager.play('helloWorld');
});
}
});
});
</script>
</head>
<body>
</body>
</html>
IE: Success; FireFox: Success
BarDev
The problem here is not the use of jquery in particular but the fact, that you are binding the configuration of Soundmanager2 to the DOM-ready event. Soundmanager2 itself also binds its loading routines to this event, so if your configuration code is not executed until this event occurs, it could already be too late, depending on the ordner in which the browser calls those event handlers.
I am no SM2 expert (never used it), but I've just stumbled upon a "lazy loading" feature of the manager, that could help, as it should allow you to defer the loading process of Soundmanager2 and explicitly load it after your configuration code has been called:
http://www.schillmania.com/projects/soundmanager2/doc/getstarted/#lazy-loading
i recently released an audio player framework that leverages SoundManager2 AND integrates w/ jQuery as a $.fn. -- very simple to use and/or customize.. similar to jPlayer in the front-end design, but w/ additional playback capabilities (eg RTMP streaming, full HTML5 support), examples, tests etc:
https://github.com/APMG/APMPlayer
I have tried it using jQuery but it is not working.
<script>
$("a").click(function () {
$.post("http://www.example.com/trackol.php", {result: "click"
}, "html");
});
</script>
out
To get the best results you should change two things in your approach
Use onmousedown instead of click - this way you get a few extra milliseconds to complete the tracking request, otherwise the browser might not start the connection to your tracker at all as it is already navigating away from the original page. The downside is that you might get some false-positive counts, since the clicking user might not finish the click (eg. keeps the mousebutton down and moves the cursor away from the link) but overall it's a sacrifice you should be willing to make - considering the better quality of tracking.
Instead of an Ajax call ($.post('...')) use an image pre-fetcher (new Image().src='...'). The fact that the tracker is not an image is not relevant in this case because you don't want to use the resulting "image" anyway, you just want to make a request to the server. Ajax call is a two way connection so it takes a bit more time and might fail if the browser is already navigating away but the image pre-fetcher just sends the request to the server and it doesn't really matter if you get something back or not.
So the solution would be something like this:
<script>
$(document).ready(function() {
$("a").mousedown(function (){
new Image().src= "http://www.example.com/trackol.php?result=click";
});
});
</script>
out
Instead of using JavaScript to call a php tracking script, you could just link to your tracking script directly and have it in turn redirect the response to the ultimate destination, something like this:
out
and in the PHP script, after you do your tracking stuff:
...
header("Location: $dest");
As mentioned, the problem is you’re not running the script after the DOM has loaded. You can fix this by wrapping your jQuery script inside $(function() { }, like so:
This works:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Tracking outgoing links with JavaScript and PHP</title>
</head>
<body>
<p>Test link to Google</p>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script>
<script>
$(function() {
$('a').click(function() {
$.post('http://www.example.com/trackol.php', { result: 'click' }, 'html');
});
});
</script>
</body>
</html>
See it in action here: http://jsbin.com/imomo3