Questions on use of image.src for statistic - javascript

I have a problem that when I was using image.src to make a request on a 1x1 gif. But when I use chrome and safari, the request cannot be logged in my web server. But when I use a javascript to delay for some mini-second(let's say 300ms), it works then. Do any one know a better solution instead of using 300ms delay(as it makes my click become slower)?
My javascript looks like that
/* Event Capture */
function eventCapture(et,ep,eid,eurl) {
var ec=new Image();
ec.src=cp+cd+cu
+"&et="+escape(et)
+"&ep="+escape(ep)
+"&ei="+escape(eid)
+"&eu="+escape(eurl)+_do+_vo
+"&cb="+new Date().getTime();
}
Does anyone know the reqson?

How are you setting the delay? If you just call
setTimeout("javascript function",milliseconds);
on the event capture and put the delay in there, it shouldn't block the rest of your page and will be able to handle multiple clicks at once since each call would call a new setTimeout that is independent from the rest.
As far as your code is concerned, I'm assuming the variables cp, cd, and cu are defined globally elsewhere? If something isn't defined when the script is first called, Chrome will throw a ReferenceError while you're creating the src string of the image, which could be what is throwing everything off.

One possibility is that the image is garbage collected immediately because there's no lasting reference to the image variable ec. But, when you do a setTimeout(), it creates a closure that make the variable last long enough.
You could test this solution that creates a temporary closure only until the image actually loads to see if it solves the problem:
/* Event Capture */
function eventCapture(et,ep,eid,eurl) {
var ec=new Image();
ec.onload = function() {
ec = null;
}
ec.src=cp+cd+cu
+"&et="+escape(et)
+"&ep="+escape(ep)
+"&ei="+escape(eid)
+"&eu="+escape(eurl)+_do+_vo
+"&cb="+new Date().getTime();
}

Related

Potential errors in asynchronous loading of images in photo viewer (Javascript and jQuery)

I'm going through a book called "Javascript & jQuery" by Jon Duckett. In it, there's an example of a photo viewer that can be found here:
http://javascriptbook.com/code/c11/photo-viewer.html
In the book, he states that
because larger images take longer to load, if a user clicks on a two different images in quick succession: 1) The second image could load faster than the first one and be displayed in the browser. 2) It would be replaced by the first image the user clicked on when that image had loaded. This could make users think the wrong image has loaded.
I get this concept, however, the code he cites does not seem to fix the problem, at least not in my eyes. Here is an abbreviated version of the full script, which can be found here: http://javascriptbook.com/code/c11/js/photo-viewer.js (his version is much better commented)
var request;
var $current;
var cache = {};
var $frame = $('#photo-viewer');
var $thumbs = $('.thumb');
// Other code goes here...
$(document).on('click', '.thumb', function(e){ // User clicks on thumbnail
var $img;
var src = this.href;
request = src;
// Other code goes here...
$img = $('<img/>');
cache[src] = {
$img: $img,
isLoading: true
};
$img.on('load', function() { // Code to run when image loads
$img.hide();
$frame.removeClass('is-loading').append($img);
cache[src].isLoading = false;
if (request === src) { // Check to make sure the image that is loading is the most recently requested image
crossfade($img); // Call function to load new image
}
});
$img.attr({
'src': src,
'alt': this.title || ''
});
So basically his solution is to create a global "request" variable and a local "src" variable. When a user clicks on a thumbnail, the request variable is assigned the value of src, which is the path to the image being requested. Later on, when the image loads, that request variable is checked to ensure that it equals src.
The logic behind the line that performs the check seems faulty to me. However, I'm new to programming and I'm not sure if there's a concept I'm not fully understanding. The line in question is this one:
if (request === src) {
Since the request variable was assigned the value of the src variable earlier in the script, they would still be the same, even when an old image loads after the new one has been clicked. The only difference is that value of each would be different.
Wouldn't the following be the right way to check this?
if (request === this.src) {
That way if the old image loads after the most recently requested image, the request variable would have stored the most recent path, and the old image's src attribute would no longer match it.
This might be pretty confusing, since I didn't include the whole script, so let me know if you need any elaboration. I tried to only include the absolutely critical aspects of the code so that you wouldn't have to go through all of it to understand my question.
Thank you for any help!
The code in the book is correct. The trick here is to have a variable across all instances of the callback that contains the most recently clicked href.
|---1----------...loading...------------cb------------|
|---2--...loading...--cb------------------------------|
|---3-----...loading...-----------cb------------------|
If this were the timeline of three clicks...we click a large image first (longer loading time before callback (cb)) and then we click a second, smaller image, finally followed by a medium image. We would have three click events all firing at the same time. The first callback we'd get is the 2nd click, followed by the third click, followed by the first click. However, since our request variable is outside the scope of our callback, it's value will be equal to the last thing it was set to, in this case, the href of click three.
I think all this logic makes sense to you but it's the timing you're not understanding. When click one happens, we set the external variable request to href1, when click two happens, we set it to href2 and then finally click three sets it to href3. These things all happened prior to the .on('load') callback which means, when image two loads first and we check the value of request, it's already been set to href3 which is !== src (href2), once image 3 loads next in the timeline, we see that this is the image we expect so we load it. Some few odd seconds/minutes later when image one calls it's callback, we see that request is not the href for image 1 and we don't show image one.
Either your code or the suggested code should work. The key you are missing is the scopes of the two variables.
var request;
$(document).on('click', '.thumb', function(e){ // User clicks on thumbnail
var $img;
var src = this.href; //variable exists only inside this onclick callback
request = src;
request exists outside of the enclosure so every time an image is clicked, its value gets overridden and stored.
src is declared inside of the onclick callback function and initialized with the link href. If another image is clicked while the first is loading, a new src variable is created to store the new data. As far as the onload callback is concerned, src doesn't change at that scope. src remains the value that it was first set at (the href of the link) and isn't overridden by following clicks.

Cocos2d-html5 - Restarting/quitting engine possible?

With Cocos2d-html5, is there any way to “quit” the engine and re-initialize its state so you can later restart the engine. We are using a Cocos2d game in a single page web app. If the user navigates to another page, we want to programmatically remove the canvas div and attempt to exit the Cocos2d engine. Unfortunately, it seems there’s no good way to do this and attempting to load a new game causes errors.
How can you cleanly unload a scene and quit the engine?
We are using V3.1 and tried several approaches, none of them seem to work. For example:
Trying to reset many variables that are held in the cc object (detailed below). This approach forces you to reset variables that look private as they start with _ and are not in the documentation.
picking, choosing and rewriting some of CCBoot.js but this appeared complicated and not sustainable for engine updates as so much of it is depended upon throughout the library.
Other approaches I thought of but all sound like a hack:
3. Null the whole cc object and somehow run the script again but that might mean stripping out a script tag or module and adding and running it again.
4. Wrap the cc object in another object so it is easier to reset. CCBoot.js looks like it might attach some things to the window object.
I have got furthest with the first approach but am stuck with context issues. When leaving the canvas, before I remove it from the DOM I call these:
// Remove anything that might possibly keep a reference to the old context
cc.textureCache.removeAllTextures();
cc._drawingUtil = null;
cc.stencilBits = null;
// Purge the cc.director, schedules, event listeners, running scene, animations, cached data.
cc.director.purgeDirector();
// Remove references to the context
cc._renderContext = null;
cc.webglContext = null;
window.gl = null;
cc._mainRenderContextBackup = null;
// Remove reference to DOM elements
cc._canvas = null;
cc.container = null;
cc._gameDiv = null;
// Reset CCBoot variables that might stop us from re-initialising
cc._rendererInitialized = false;
cc._setupCalled = false;
cc._renderType = -1;
Then when we restart on the second or subsequent time call
// Reset all system and flag variables
cc._initSys(cc.game.config, cc.game.CONFIG_KEY);
cc.game.run();
And here are the kind of errors I get. It looks like it's not properly resetting the context:
WebGL: INVALID_OPERATION: bindTexture: object not from this context
WebGL: INVALID_OPERATION: texImage2D: no texture
WebGL: INVALID_OPERATION: uniformMatrix4fv: location is not from current program
WebGL: INVALID_OPERATION: vertexAttribPointer: no bound ARRAY_BUFFER
I've managed to get it stopping/resuming enough for a specific use case now, here is a demo:
http://plnkr.co/edit/BQmmHi?p=preview
There is now a public way to pause a game in v3.13, so I can call:
cc.game.pause();
followed by removing the cc.container from the DOM:
cc.container.parentNode.removeChild(cc.container);
Then I can place it back in the DOM again anywhere I would like:
targetContainerEl.appendChild(cc.container);
Then resume the game by calling:
cc.game.resume();
Note: The next line was was needed after replacing in the DOM in a complex app in cocos2d v3.5, but not sure if it's needed in v3.13 any more:
cc.EGLView._instance._frame = cc.container.parentNode;
The demo from the link above is being removed and placed in 2 different DOM elements and pausing/resuming.

Load list of images in sequential order

I'm working on a website which uses ExpressionEngine to create a list of images with img1, img2, img3 etc as the ID and creates an array with their sources imgAddresses[1], imgAddresses[2], imgAddresses[3] etc.
I'm attempting to create a function which loads the first image, then (when the first is completely loaded), load the second, third etc. The following is what I have so far:
function loadImage(counter) {
var i = document.getElementById("img"+counter);
if(counter==imgAddresses.length) { return; }
i.onload = function(){
loadImage(counter+1)
};
i.src = imgAddresses[counter];
}
document.onload=loadImage(0);
It works when refreshing the page, but not when accessing the page via the URL. As far as I can tell from research, this is because the onload event is not fired when a cached image is loaded, and refreshing the page clears the cache, whereas accessing the page via the URL does not.
Research suggests that assigning the src of the image after declaring the onload event would get around this, but it does not seem to have solved it in this case. I was thinking that this may be because the onload event is recursive in this case.
Does anyone have any ideas on how to make sure the browser is loaded a fresh copy of the image, rather than a cached version? Or whether there is a better way to write this function? Thanks for any help!
EDIT: One solution that I have found is to change the img source assignment to:
i.src = imgAddresses[counter] + '?' + new Date().getTime();
This forces the user to load a fresh copy each time, which I guess is not so much a solution, but a workaround
The only thing I can say is that you are not attaching the document.onload handler correctly. I cannot tell if it will fix your issue because image.onload is not reliable in all browsers, however the onload should be set to a function reference and that's not what you are doing.
Instead, you should have something like:
function loadImage(counter) {
//initialize the counter to 0 if no counter was passed
counter = counter || 0;
var i = document.getElementById("img"+counter);
if(counter==imgAddresses.length) { return; }
i.onload = function(){
loadImage(counter+1)
};
i.src = imgAddresses[counter];
}
document.onload = loadImage; //instead of loadImage(0);
You can tell how the browser will manage the cached resources
Take a look to HTML5 cache approach:
HTML5: The cache manifest file
This way you can avoid the browser cache for the specified resources.

Event when navigateToURL has finished in flex

I am using navigateToURL for file downloading from server. Is there anyway possible to know when navigateToURL has finished, more like when browser download dialog has opened?
Sometimes it takes 5 seconds to complete, user might get confused and start clicking download button like psychopath, which can result in multiple download dialogs opened.
I want to add some "please wait" text or something before it finishes (I already have one, I just need to know when to stop).
Maybe it can be done using javascript and get info from ExternalInterface?
This is kind of crazy, but I can't think of any other way: you could put the request object into a dictionary which is set to weak reference the keys, and then check on intervals whether the key was removed.
However, I'm not sure what will happen first, either the SWF itself will be disposed or the dictionary will be cleaned. It's also possible that given the one-time-ness of the function the reference to the request object isn't deleted because it is assumed to be deleted together with the whole SWF.
One more thing that I know is that uncaught error events will catch events from navigateToURL - not really helpful, but at least may give you the indication if it didn't work.
One more simple thing I can think of - just disable the button for a short time, like 1-2 seconds. If it worked, no one will see the delay, and if it didn't, they won't be able to press it too often.
private var _requestStore:Dictionary = new Dictionary(true);
private var _timer:Timer = new Timer(10);
. . .
_timer.addEventListener(TimerEvent.TIMER, timerHandler);
. . .
public function openURL(url:String):void
{
var request:URLRequest = new URLRequest(url);
_requestStore[request] = true;
_timer.start();
navigateToURL(request);
}
private function timerHandler(event:TimerEvent):void
{
var found:Boolean;
for (var o:Object in _requestStore)
{
found = true;
break;
}
if (!found) // the request got disposed
}
Is there anyway possible to know when navigateToURL has finished, more
like when browser download dialog has opened?
No, there is not. Once you pass a request onto the browser; the Flash Player no longer has any control or access to it.
You mention using ExternalInterface as part of a possible solution, but how would your HTML/JavaScript page know that a download had been finished?
navigateToURL does not fire a complete event. Try using this event from Adobe's help documentation:
public function URLRequestExample() {
loader = new URLLoader();
configureListeners(loader);
var request:URLRequest = new URLRequest("XMLFile.xml");
try {
loader.load(request);
} catch (error:Error) {
trace("Unable to load requested document.");
}
}
private function configureListeners(dispatcher:IEventDispatcher):void {
dispatcher.addEventListener(Event.COMPLETE, completeHandler);
dispatcher.addEventListener(Event.OPEN, openHandler);
dispatcher.addEventListener(ProgressEvent.PROGRESS, progressHandler);
dispatcher.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
dispatcher.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
dispatcher.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
}
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/URLRequest.html#includeExamplesSummary

How does the javascript preloading work?

I don't want to know a way to preload images, I found much on the net, but I want to know how it works.
How is javascript able to preload images?
I mean, I tried a snippet from here, and even if it works, it doesn't seem to preload images.
When I check firebug, I can see that the image is loaded twice, once while the preloading, another time when displaying it!
To improve this code I'd like to know how it works.
Here is what i do:
function preload(arrayOfImages) {
$(arrayOfImages).each(function(){
$('<img/>')[0].src = this;
//(new Image()).src = this;
alert(this +' && ' + i++);
});
}
then i do something like that:
preloader = function() {
preload(myImages);
}
$(document).ready(preloader);
Here is how i display/add the image :
$("li.works").click(function() {
$("#viewer").children().empty();
$('#viewer').children().append('<img src=\'images/ref/'+this.firstChild.id+'.jpg\' alt="'+this.firstChild.id+'" \/>')
$("#viewer").children().fadeIn();
Your basic Javascript preloader does this:
var image = new Image();
image.src = '/path/to/the/image.jpg';
The way it works is simply by creating a new Image object and setting the src of it, the browser is going to go grab the image. We're not adding this particular image to the browser, but when the time comes to show the image in the page via whatever method we have setup, the browser will already have it in its cache and will not go fetch it again. I can't really tell you why whatever you have isn't working this way without looking at the code, though.
One interesting gotcha that is discussed in this question is what happens when you have an array of images and try preloading them all by using the same Image object:
var images = ['image1.jpg','image2.jpg'];
var image = new Image();
for(var x = 0; x < images.length; x++) {
image.src = images[x];
}
This will only preload the last image as the rest will not have time to preload before the loop comes around again to change the source of the object. View an example of this. You should be able to instantly see the second image once you click on the button, but the first one will have to load as it didn't get a chance to preload when you try to view it.
As such, the proper way to do many at once would be:
var images = ['image1.jpg','image2.jpg'];
for(var x = 0; x < images.length; x++) {
var image = new Image();
image.src = images[x];
}
Javascript preloading works by taking advantage of the caching mechanism used by browsers.
The basic idea is that once a resource is downloaded, it is stored for a period of time locally on the client machine so that the browser doesn't have to retrieve the resource again from across the net, the next time it is required for display/use by the browser.
Your code is probably working just fine and you're just misinterpeting what Fire Bug is displaying.
To test this theory just hit www.google.com with a clean cache. I.e. clear your download history first.
The first time through everything will likely have a status of 200 OK. Meaning your browser requested the resource and the server sent it. If you look at the bottom on the Fire Bug window it will says how big the page was say 195Kb and how much of that was pulled from cache. In this case 0Kb.
Then reload the same page without clearing your cache, and you will still see the same number of requests in FireBug.
The reason for this is simple enough. The page hasn't changed and still needs all the same resources it needed before.
What is different is that for the majority of these requests the server returned a 304 Not Modified Status, so the browser checked it's cache to see if it already had the resource stored locally, which in this case it did from the previous page load. So the browser just pulled the resource from the local cache.
If you look at the bottom of the Fire Bug window you will see that page size is still the same (195Kb) but that the majority of it, in my case 188Kb, was pulled locally from cache.
So the cache did work and the second time i hit Google I saved 188Kb of download.
I'm sure you will find the same thing with preloading your images. The request is still made but if the server returns a status of 304 then you will see that the image is in fact just pulled from local cache and not the net.
So with caching, the advantage is NOT that you kill off all future resource requests, i.e. a Uri lookup is still made to the net but rather that if possible the browser will pull from the local cache to satisify the need for the content, rather than run around the net looking for it.
You may be confused by the concept of "preloading". If you have a bunch of images in your HTML with <img src="...">, they cannot be preloaded with Javascript, they just load with the page.
Preloading images with Javascript is about loading images not already in the document source, then displaying them later. They are loaded after the page has rendered for the first time. They are preloaded in order to eliminate/minimize loading time when it comes to making them appear, for example when changing an image on mouse rollover.
For most applications, it is usually better practice to use "CSS sprites" as a form of preloading, in lieu of Javascript. SO should have a ton of questions on this.
It just involves making a new DOM image object and setting the src attribute. Nothing clever and AFAIK, it has always worked for me.
Is it possible the second "load" firebug is showing you is it loading it from cache?
The index on the loop is only looking
at the first image. Change it to use
the index:
function preload(arrayOfImages) {
$(arrayOfImages).each(function(i){ // Note the argument
$('<img/>')[i].src = this; // Note the i
//(new Image()).src = this;
alert(this +' && ' + i++);
});
}
Edit: In retrospect, this was wrong and I can see you're trying to create image elements. I don't understand why the index is there at all, there need not be an index. I think the function should look like this:
function preload(arrayOfImages) {
$(arrayOfImages).each(function () {
$('<img/>').attr('src', this);
});
}
And to instantiate it, why not just do this:
$(function () { // Equivalent to $(document).ready()
preload(myImages);
});
JavaScript image preloading works because when a DOM element that contains an image is created, the image is downloaded and cached. Even if another request is made when the image is actually rendered from the HTML, the server will send back a 304 (not changed), and the browser will simply load the image from its cache.
Paolo suggests using the following notation to create an image object:
var image = new Image();
While this will work, the DOM-compliant way of doing this is:
var image = document.createElement('img');
image.setAttribute('src', 'path/to/image.jpg');
Which is the way it is being done in the script, except it's using jQuery's HTML string literal syntax to do it. Additionally, most modern browsers offer compatibility with the Image() constructor by simply calling DOM-standard methods. For example, if you open up the Google Chrome JavaScript console and type Image, this is what you'll get:
function Image() {
return document.createElementNS('http://www.w3.org/1999/xhtml', 'img');
}
Chrome merely uses the native DOM methods to create an image element.

Categories