How can I do a spinlock in javascript?
I'm trying to load a bunch of images and I can only move forward after everything is loaded, so I have a spinlock like
for(...)
image[i].onload = function() { ++imagesloaded; }
while(imagesloaded != totalimages)
{
}
And it crashes my browser. Is there a better way to do it? Or a yield / sleep function I'm missing?
Short answer: don't spinlock.
Longer answer: here's how to do it:
var imagesLoaded = 0;
var totalImages = 42;
function handleImageLoad()
{
imagesLoaded++;
if (imagesLoaded === totalImages)
{
doSomething();
}
}
for (var i=0; i<totalImages; i++)
{
image[i].onload = handleImageLoad;
}
In general, when you want to sleep/wait/spin in JavaScript, instead think about solving the problem in terms of callbacks (and setTimeout/setInterval).
The answers above aren't useful as spinlocks may be required because of limitations/bugs in browsers. For instance safari (hopefully not future versions) requires the use of method window.open when you want to generate a file in javascript. The consequence of this is that you cannot generate the file using any callbacks (because of popup blockers), this in effect forces the use of a dialog window that first calls the file generation function (using callbacks) and then a button that downloads the file. Because spinlocks don't work the code becomes the following:
function process(callback) {
processCallbackData = null; // global var that must be a unique name
callback(function(data) {
processCallbackData = data;
});
}
function fowardButton() {
if(processCallbackData!=null) {
goForwardUsingCallbackIncompatibleCode();
} else {
displayStillLoadingWarning();
}
}
Don't use a loop to check. Check in the event handler function. (So you only do the check when an image has loaded, not continuously and as quickly as possible)
Related
I'm wondering if there is another way to write this function without using a modulo. I realized that I have another piece of code that requires me to click the #mail-wrap button and doing so messes up the number of clicks which affects this function.
It's just a simple switch. I'm not too good with conditionals.
$('#mail-wrap').click(function (e) {
e.preventDefault();
var c = 0;
if (c++ % 2 == 0) {
$('#contact-button').addClass('project-button').text('Projects');
} else {
$('#contact-button').removeClass('project-button').text('Get in touch');
}
});
Edit: Changed the question a bit. Sorry, the last one was too broad.
As Boldewyn mentioned, most likely your problem is that you are defining a global variable c. But if you would like to avoid this variable completely you could check for the CSS-class of contact-button via the jQuery hasClass function, i.e.
$('#mail-wrap').click(function (e) {
...
var contactButton = $('#contact-button');
if (!contactButton.hasClass('project-button')) {
$('#contact-button').addClass('project-button').css('width', '71px').text('Projects');
...
} else {
$('#contact-button').removeClass('project-button').css('width', '96px').text('Get in touch');
...
}
});
The code is interfering with other code, because you have implicitly generated a global variable c. Possible fix: Use an IIFE:
(function() {
var c = 0;
/* rest of your code above ... */
})();
I've a javascript function setErrorImages(), which searches all img tags and and gives them an onerror function. This works great.
I also have a division content with dynamic content by calling de jquery .load function like this, which also works:
$("#content").load("http://example.com/otherpage.php");
But when the new content (otherpage.php) was loaded into the division, the setErrorImages() function doesn't work on the img'es within the new content.
I can call the setErrorImages() function on the otherpage.php, and everything works perfectly, but this way I have to do this on every otherpage, which are a lot.
Is there a way to send a javascript file or function with the jquery .load,
or maybe there is a way to re-execute the javascript function right after the jquery .load.
Kind regards,
Liontack
function setErrorImages(){
var images = document.getElementsByTagName("img");
for (var i = 0; i < images.length; i++) {
images[i].onerror = function(){ setErrorImage(this); };
}
}
function setErrorImage(image){
image.onerror = null;
image.src = 'http://tanuri.eu/images/error.png';
}
$("#content").load("http://example.com/otherpage.php", setErrorImages);
.load() accepts a "complete" parameter:
A callback function that is executed when the request completes.
Firstly, I suggest using the this context instead of a parameter for your error handler:
function setErrorImage() {
this.onerror = null;
this.src = 'http://tanuri.eu/images/error.png';
}
Secondly, change setErrorImages to use this too:
function setErrorImages() {
var images = this.getElementsByTagName('img');
for (var i = 0, n = images.length; i < n; ++i) {
images[i].onerror = setErrorImage; // parameter is now implict
}
}
Note how there's now no need for a function wrapper for the onerror call - the browser automatically sets this to be the element of interest.
For simpler code you could also just use jQuery to do this job for you since you already have it loaded:
function setErrorImages() {
$('img', this).error(setErrorImage);
}
Thirdly, use the complete handler of .load, taking advantage of the fact that it sets this to be the parent of the part of the DOM that just got replaced. This avoids setting the onerror handler on elements in the page that you already looked at.
$('#content').load(url, setErrorImages);
The only other change is that on your initial invocation you'll need to pass document to setErrorImages using .call:
setErrorImages.call(document);
i am sending FB.login request to facebook. but FB is not defined while javascript SDK is still loading core javascript resources.
so, i put a check to get FB variable
function check_FB_variable(){
if(typeof FB=='undefined'){
check_FB_variable();
}else{}
}
check_FB_variable();
But this approach gives me Too much recursion error.
so , i put this code as
function check_FB_variable(){
if(typeof FB=='undefined'){
setTimeout(check_FB_variable,600);
}else{}
}
check_FB_variable();
but in this approach the before timeout function make a call function moves down and gives error
FB.login not defined.
please, help.
I've used something similar to check if JQMobi exists, I don't know exactly why but I think the exception is thrown because you call the pointer to the function every time.
You should try checking in an interval like this (Untested):
var facebookChecker = window.setInterval(fbCheck, 200);
var fbCheck = function () {
if (typeof FB != 'undefined' && facebookChecker != null) {
window.clearInterval(facebookChecker);
facebookChecker = null;
// Whatever you want to do if facebook is loaded
// Example: InitFBLogin();
}
}
Or you could use a while statement (the one I used):
/*
* This JQ Fix tries to attach Jquery to a variable to ensure it exists.
* - Marvin Brouwer.
*/
var FixJQ = function () {
var JQFIX = null;
while (!JQFIX || JQFIX == null) {
try {
JQFIX = jQuery;
} catch (nothing) { jQuery = $; };
};
JQFIX = null;
return true;
};
if (FixJQ()) {
FixJQ = null;
};
The beauty of the last one is that you can put you next step below this, because it will wait until the while loop has finished.
I honestly do not know which one is better/faster but I’m sure the bottom one will work.
There seems to be little support or discussion around regarding Google Swiffy (http://swiffy.googlelabs.com/).
Is it possible to effectively pause/resume/manipulate a swiffyobject from JS?
Using standard Google output, I noticed the swiffyobject could be found in console with a few properties; notably frameRate. Could this property be manipulated for example?
For the latest Swiffy release (Swiffy runtime version 5.2 https://www.gstatic.com/swiffy/v5.2/runtime.js) I did this.
1.Use jsbeautifier.org as mentioned in samb's post.
2.Find the function containing .start(). In my case...
db(N, function () {
var a = this.Dg;
this.ck(function () {
a.start()
})
});
db(Yj[I], Yj[I].start);
3.Duplicate this function with a different name, and replace start() with stop()
myNewFunction(N, function () {
var a = this.Dg;
this.ck(function () {
a.stop()
})
});
myNewFunction(Yj[I], Yj[I].stop);
4.Find the declaration of the function containing .start(). In my case db.
function db(a, b) {
return a.start = b
}
5.Duplicate this function and call it the same as the new function you created with stop() in and replace start with stop. In my case myNewFunction.
function myNewFunction(a, b) {
return a.stop = b
}
That's it.
Now you can call my anim.stop();
e.g.
//create anim
var anim = {swiffy code};
var myAnim = new swiffy.Stage(document.getElementById('animContainer'), anim);
myAnim.start();
//some button click
myButton.on('click',function(){
myAnim.stop();
});
Sorry for my english I'm french;)
I was looking for a solution to be able to properly handle animation Swiffy.
Since the new version (5.0) google code has changed and I can no longer maniupler animation with small hacks found on the net ...
For cons, I coded force to find a solution .. which seems to me very simple and clean .. (without touching the source Swiffy!)
In fact any part of this post : swiffy / javascript
Can be recovered with flashvars Swiffy (in as2 and as3 it should work too ..)
the side javascript can do this kind of things:
function playMovie(){
stage.setFlashVars('myresponse=play');
return false;
}
function stopMovie(){
stage.setFlashVars('myresponse=pause');
return false;
}
and the side of the flash in a function enterFrame ... :
_root.onEnterFrame = function(){
switch(_level0.myresponse){
case 'play':
_root.play();
break;
case 'pause':
_root.stop();
break;
default :
break;
}
_level0.myresponse = undefined;
}
and that's it!
To you organize the methods you want but .. it works;)
Must retake the undefined variable if you want to reuse it later ;)
Having un-minified the runtime.js - it was possible to achieve the behaviour I wanted.
On line 3312 (unminified - jsbeautifier.org)
M.start = function (arg) {
this.T[Qa]();
if(arg){
this.cb.start(arg)
}else{
this.cb.start()
}
};
And on line 3823:
M.start = function(arg) {
if(arg){
console.log(arg);
window.clearInterval(window.pauseAnimation)
}else{
window.pauseAnimation = window.setInterval(Ob(this.ne, this), 40 );
if (!this.ie) this.ie = !0, this.ne(), window.pauseAnimation
}
};
Then using console, it is possible to pause/resume your animation using:
stage.start(true) // PAUSE the animation.
stage.start() // RESUME the animation.
I have a javascript plugin for a special image scroller. The scroller contains a bunch of timeout methods and a lot of variables with values set from those timeouts.
Everything works perfectly, but for the site I am working on it is required that the pages are loaded dynamically. The problem with this is when i for instance change the language on the site this is made by jquery load function meaning the content is dynamically loaded onto the site - AND the image slider aswell.
NOW here is the big problem! When I load the image slider for the second time dynamically all my previous values remains as well as the timers and everything else. Is there any way to clear everything in the javascript plugin as if it where like a page reload?
I have tried a lot of stuff so far so a little help would be much appreciated!
Thanks a lot!
You might want something like that to reload scripts:
<script class="persistent" type="text/javascript">
function reloadScripts()
{ [].forEach.call(document.querySelectorAll('script:not(.persistent)'), function(oldScript)
{
var newScript = document.createElement('script');
newScript.text = oldScript.text;
for(var i=0; i<oldScript.attributes.length; i++)
newScript.setAttribute(oldScript.attributes[i].name, oldScript.attributes[i].value);
oldScript.parentElement.replaceChild(newScript, oldScript);
});
}
// test
setInterval(reloadScripts, 5000);
</script>
As far as I know, there's no other way to reset a script than completely remove the old one and create another one with the same attributes and content. Not even clone the node would reset the script, at least in Firefox.
You said you want to reset timers. Do you mean clearTimeout() and clearInterval()? The methods Window.prototype.setTimeout() and Window.prototype.setInterval() both return an ID wich is to pass to a subsequent call of clearTimeout(). Unfortunately there is no builtin to clear any active timer.
I've wrote some code to register all timer IDs. The simple TODO-task to implement a wrapper callback for setTimeout is open yet. The functionality isn't faulty, but excessive calls to setTimeout could mess up the array.
Be aware that extending prototypes of host objects can cause undefined behavior since exposing host prototypes and internal behavior is not part of specification of W3C. Browsers could change this future. The alternative is to put the code directly into window object, however, then it's not absolutely sure that other scripts will call this modified methods. Both decisions are not an optimal choice.
(function()
{ // missing in older browsers, e.g. IE<9
if(!Array.prototype.indexOf)
Object.defineProperty(Array.prototype, 'indexOf', {value: function(needle, fromIndex)
{ // TODO: assert fromIndex undefined or integer >-1
for(var i=fromIndex || 0; i < this.length && id !== window.setTimeout.allIds[i];) i++;
return i < this.length ? i : -1;
}});
if(!Array.prototype.remove)
Object.defineProperty(Array.prototype, 'remove', { value: function(needle)
{ var i = this.indexOf(needle);
return -1 === i ? void(0) : this.splice(i, 1)[0];
}});
// Warning: Extensions to prototypes of host objects like Window can cause errors
// since the expose and behavior of host prototypes are not obligatory in
// W3C specs.
// You can extend a specific window/frame itself, however, other scripts
// could get around when they call window.prototype's methods directly.
try
{
var
oldST = setTimeout,
oldSI = setInterval,
oldCT = clearTimeout,
oldCI = clearInterval
;
Object.defineProperties(Window.prototype,
{
// TODO: write a wrapper that removes the ID from the list when callback is executed
'setTimeout':
{ value: function(callback, delay)
{
return window.setTimeout.allIds[window.setTimeout.allIds.length]
= window.setTimeout.oldFunction.call(this, callback, delay);
}
},
'setInterval':
{ value: function(callback, interval)
{
return window.setInterval.allIds[this.setInterval.allIds.length]
= window.setInterval.oldFunction.call(this, callback, interval);
}
},
'clearTimeout':
{ value: function(id)
{ debugger;
window.clearTimeout.oldFunction.call(this, id);
window.setTimeout.allIds.remove(id);
}
},
'clearInterval':
{ value: function(id)
{
window.clearInterval.oldFunction.call(this, id);
window.setInterval.allIds.remove(id);
}
},
'clearTimeoutAll' : { value: function() { while(this.setTimeout .allIds.length) this.clearTimeout (this.setTimeout .allIds[0]); } },
'clearIntervalAll': { value: function() { while(this.setInterval.allIds.length) this.clearInterval(this.setInterval.allIds[0]); } },
'clearAllTimers' : { value: function() { this.clearIntervalAll(); this.clearTimeoutAll(); } }
});
window.setTimeout .allIds = [];
window.setInterval .allIds = [];
window.setTimeout .oldFunction = oldST;
window.setInterval .oldFunction = oldSI;
window.clearTimeout .oldFunction = oldCT;
window.clearInterval.oldFunction = oldCI;
}
catch(e){ console.log('Something went wrong while extending host object Window.prototype.\n', e); }
})();
This puts a wrapper method around each of the native methods. It will call the native functions and track the returned IDs in an array in the Function objects of the methods. Remember to implement the TODOs.