JS wait for content to finish loading before running function - javascript

I'm running a webpage in some graphics playout software. The function update() is automatically called by the graphics software when the page initially loads up, however I need to wait for the animation library to finish loading before the code in update() executes.
animation.addEventListener('DOMLoaded', function () {
console.log('Animation ready');
});
// This is automatically called by the graphics renderer on page load.
function update(data) {
// update the data in the animation...
};
I've come up with a workaround using setInterval that check if the animation has loaded before running the update code:
var animationLoaded = false;
animation.addEventListener('DOMLoaded', function() {
console.log('Animation ready');
animationLoaded = true;
});
// This is automatically called by the graphics renderer on page load.
function update(data) {
var updateInterval = setInterval(function() {
if (animationLoaded) {
clearInterval(updateInterval);
// update the data in the animation...
}
}, 10);
}
I feel there is a much better way of doing this, maybe using async/await? but I am unsure how to do this?

An idea, may not be the best :
on the update function, if DOMLoaded has been trigger then do your job, else save the data parameter
on the DOMLoaded function, call update with the saved datas (if set)
var animationLoaded = false;
var animationData = null;
animation.addEventListener('DOMLoaded', function() {
animationLoaded = true;
if (animationData !== null) {
update(animationData);
}
});
// This is automatically called by the graphics renderer on page load.
function update(data) {
if (animationLoaded) {
// update the data in the animation...
} else {
animationData = data;
}
}

Related

How to start javascript setinterval once cleared?

I have a setinterval that runes every 5 seconds. this works fine on page load.
I have the following scenarios:
Load page with interval (WORKS)
press button and load new content and stopp interval(WORKS)
Once the new content is no longer desiered, dissmiss it, return to first content and start interval again(DOES NOT WORK)
I have saftys suchs as events for window.blur that also stops the interval so that the browser does not commponsate for all the missing intervals if i would change tabs or something. Keep in mind that step 3 did not work BUT if i would after step 3 change a tab and then return to my original page(execute blur) the interval would start working again.
NOTE all content loading here exept page load is done with ajax calls.
My code:
initializing:
$.automation.worker.bindIntervalEvent("#TanksContent", "/Tank/GetTanks", function() {
$.automation.tanks.tableInit();
});
binding function:
bindIntervalEvent: function (target, url, callback) {
$(window)
.on("focus.mine",
function() {
$.automation.worker.setUpdateInterval(target, url, callback);
})
.on("blur",
function() {
$.automation.worker.stopUpdateInterval();
}).trigger("focus.mine");
}
interval function:
setUpdateInterval: function (target, url, callback) {
if ($.automation.globals.globalInterval.value.length === 0) {
$.automation.globals.globalInterval.value.push(window.setInterval(
function () {
var options = {
loadTarget: target
}
$.automation.worker.getView(url,
function() {
if (callback)
callback();
},
options);
},
5000));
}
}
the function that stops the interval:
stopUpdateInterval: function () {
if ($.automation.globals.globalInterval.value.length === 0)
return;
console.log("deleting");
for (var i = 0; i <= $.automation.globals.globalInterval.value.length; i++) {
window.clearInterval($.automation.globals.globalInterval.value[i])
$.automation.globals.globalInterval.value.splice(i, 1);
console.log($.automation.globals.globalInterval.value.length);
}
}
when stopping the interval i also remove the window bindings:
unBindIntervalEvent: function() {
$(window).off("focus.mine");
$(window).unbind("blur");
}
Back to step 3:
My sucess method in the callback to my getviewfunction is identical to what i execute in the beginning
code:
$(".updatelatest")
.on("click",
function () {
var _this = $(this);
var options = {
loadTarget:"#TanksContent"
}
$.automation.worker.getView("/Tank/GetTanks",
function (data) {
$(_this).switchClass("col-md-5", "col-md-1", 1000, function() {
$(_this).addClass("hidden");
$(".search").switchClass("col-md-5", "col-md-12", 1000, "easeInOutQuad");
})
$.automation.tanks.tableInit();
$.automation.worker.bindIntervalEvent("#TanksContent", "/Tank/GetTanks", function () {
$.automation.tanks.tableInit();
});
$(window).trigger("blur");
}, options);
});
but this does not start the interval. it is clearly initialized since it works when window.blur is executed for example when I change tab but for some reason this is not working beyond that.
i tried triggering the windows blur event and nothing happened, i tried triggering my custom window event "focuse.mine" but nothing happens.
I did not notice this while developing since I had firebug open and every time i checked scripts or css or the console the blur function was executed so I assumed that my code worked as intended but now that it is deployed I notice this.
My head is pounding beyond reason and I can't for figure out where I have gone wrong.
Well this was a fun one. I simply found that when calling the setUpdateInterval(); function directly it gave me the desiered result.
I realized that the reason I had them split like I did was becaouse of the blur event. "Focus.mine" is triggered to start the inteval again ocne a user comes back to the page.

Javascript & Soundcloud Widget: How to load new track into SC Widget iFrame (via URL)

I’ve seen different web apps like Playmoss, Whyd, and Songdrop etc. that, I believe, HAVE to utilize the Soundcloud Embedded Widget in order to produce the functionality of playing multiple tracks, in sucession, not apart of a set/(playlist). Currently I am having issues reproducing this functionality with the following library, so I decided to attempt to write my own:
https://github.com/eric-robinson/SCLPlayer
I am very new to writing javascript, but my below code, will load a first track, and play it once hitting the “ready” bind. Once hitting the “finish” bind, It will then jump to the loadNextTrack() function and load the next tracks URL, into the src of the widget’s iFrame. After that, it doesn’t ever hit the original “ready” bind, which would then begin playback.
So to clear things up, playback doesn’t begin for the second track.
<script type = "text/javascript">
var SCLPlayer = {
isPlayerLoaded : false,
isPlayerFullLoaded : false,
needsFirstTrackSkip : true,
isPaused: true,
scPlayer : function() {
widgetContainer = document.getElementById('sc');
widget = SC.Widget(widgetContainer);
return widget;
},
loadNextTrack : function() {
var ifr = document.getElementById('sc');
ifr.src = 'http://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/231758952';
console.log ('Loading Next Track');
SCLPlayer.scPlayer().bind(SC.Widget.Events.READY, function() {
console.log ('Player is Ready, next Track');
SCLPlayer.scPlayer().play();
});
}
};
$( '#sc' ).ready(function() {
SCLPlayer.scPlayer().bind(SC.Widget.Events.READY, function() {
SCLPlayer.isPlayerLoaded = true;
//window.location = 'sclplayer://didLoad';
console.log ('Player is Ready');
SCLPlayer.scPlayer().play();
});
SCLPlayer.scPlayer().bind(SC.Widget.Events.PLAY, function() {
SCLPlayer.isPaused = false;
//window.location = 'sclplayer://didPlay';
console.log ('Player did Play');
});
SCLPlayer.scPlayer().bind(SC.Widget.Events.PAUSE, function() {
SCLPlayer.isPaused = true;
//window.location = 'sclplayer://didPause';
console.log ('Player did Pause');
});
SCLPlayer.scPlayer().bind(SC.Widget.Events.FINISH, function() {
SCLPlayer.isPaused = true;
//window.location = 'sclplayer://didFinish';
console.log ('Player did Finish');
SCLPlayer.loadNextTrack();
});
});
</script>
</head>
<body>
<iframe id = "sc" width="100%" height="100%" scrolling="no" frameborder="no" src="http://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/226183306"></iframe>
</body>
The whole point of me writing this Javascript is so that I can then use a Swift to Javascript bridge in my iOS app to then control the loading of tracks into the embedded players. For some reason over a slower connection, the next track doesn't always load into the player, using the "bridge". I hope to provide the nextTrackURL to the javascript side of things before the currentTrack finishes, so that the bridge conveys nothing and the Javascript handles new track loading, solely on its own.
I think you want to use the load function to specify the url for the new track
From the soundcloud Widget API docs:
load(url, options) — reloads the iframe element with a new widget specified by the url. All previously added event listeners will continue working. options is an object which allows you to define all possible widget parameters as well as a callback function which will be executed as soon as new widget is ready. See below for detailed list of widget parameters.
var url = "https://api.soundcloud.com/";
var options = [];
// if a track
url += "tracks/";
// if a playlist
url += "playlists/"
// append the id of the track / playlist to the url
url += id;
// set any options you want for the player
options.show_artwork = false;
options.liking = false;
options.auto_play = true;
widget.load(url, options, OPTIONAL_CALLBACK_FUNCTION);
Edited to show binding...
The bind code is called once, after the widget is initially loaded.
The ready event is only called once, when the widget is initially loaded, it is not called for each subsequent call using load().
try {
widget.bind(SC.Widget.Events.FINISH,
function finishedPlaying() {
// your code / function call
}
);
widget.bind(SC.Widget.Events.PAUSE,
function paused() {
// your code / function call
}
);
widget.bind(SC.Widget.Events.PLAY,
function playing() {
// your code / function call
widget.getCurrentSound(function scCurrentSound(sound) {
// this also binds getCurrent sound which is called
// each time a new sound is loaded
});
}
);
widget.bind(SC.Widget.Events.PLAY_PROGRESS,
function position(pos) {
// your code / function call
}
);
widget.bind(SC.Widget.Events.SEEK,
function seek(pos) {
// your code / function call
}
);
widget.bind(SC.Widget.Events.READY,
function ready() {
// your code / function call
}
);
} catch(e) {
// exception handler code
}

Universal button handler in javascript

I have a lot of buttons on my web app that request and post data to PHP to retrieve and update a database. I am struggling to create a universal way to prevent multiple button clicks when submitting forms, because I am using AJAX and Jquery.
This is my current implementation but I can't even tell if it works. It seems to work 99% of the time.
In my common functions.js file I have this function which is in the global scope
var canClick = true;
function buttonWithPromise(promise){
if(!canClick) return;
canClick = false;
promise.done(function(){
canClick = true;
});
}
Then any time I attach a .click to a dom element I do it like this:
$('body').on('click', '.table > .row', function(){
var nbr = $(this).attr('nbr');
buttonWithPromise(get_count(nbr));
});
And some function that might be called will have a deferred object.
function get_count(){
var defer = $.Deferred();
var options = "getCount"
Query.init(options)
.fetchData(function(data){ //Ajax data request
if(data){
}
defer.resolve();
});
return defer.promise();
}
Since this only sometimes works, I can tell it's wrong. Any advice for improvements?
Everything in Javascript is an object, yes? So why not:
$('body').on('click', '.button', function()
{
// Set default value of property
if(typeof this.isClicked === 'undefined')
this.isClicked = false;
// Check if button is working
if(this.isClicked)
{
// Send error to console if button is busy
console.log('Cannot click as a network action is occuring!');
}else
{
// Begin new network action if button is not busy
var self = this;
console.log('Begin network for: ' + $(this).text());
this.isClicked = true;
setTimeout(function()
{
// Reset button state once network action is done
console.log('End network for: ' + $(self).text());
self.isClicked = false;
//Call any callbacks/promises here
}, 5000);
}
});
Fiddle:
http://jsfiddle.net/mdLfug1t/
NOTE: I'm using setTimeout to simulate an ajax request
EDIT: Let me put this more into context:
function buttonWithPromise(promise)
{
if(typeof promise.canClick === 'undefined')
promise.canClick = true;
if(!promise.canClick) return;
promise.canClick = false;
promise.done(function()
{
promise.canClick = true;
});
}
The problem that you're running into is that "canClick" is global and so gets modified by every promise. You need to make it a property of a promise so that you can create infinite promises, each with their own instance of canClick.

Detect if script has already loaded or not

It seems that helloworld.js gets loaded multiple times based on the number of times I click #load. I say this because when I look at Google Chromes Developer Tools Network tab, it shows helloworld.js as many times as I click #load.
$(document).ready(function() {
$("#load").click(function(){
$.getScript('helloworld.js', function() {
hello();
});
});
});
The hello() function looks like this:
function hello(){
alert("hello");
}
Is it possible to detect if helloworld.js has already loaded?
So if it hasn't loaded, load it, and if it has loaded, don't load it.
This is what Developer Tools currently shows me if I click the #load button 4 times:
Set a flag when file loaded successfully. If flag is set then skip the file loading again.
Try this code,
var isLoaded = 0; //Set the flag OFF
$(document).ready(function() {
$("#load").click(function(){
if(isLoaded){ //If flag is ON then return false
alert("File already loaded");
return false;
}
$.getScript('helloworld.js', function() {
isLoaded = 1; //Turn ON the flag
hello();
});
});
});
So why not only fire the event once like this:
$("#load").one("click", function() {
$load = $(this);
$.getScript('helloworld.js', function() {
hello();
// bind hello to the click event of load for subsequent calls
$load.on('click', hello);
});
});
That would prevent subsequent loads and avoids the use of a global
Another option is letting .getScript() run but let it take the script from browser's cache so you won't have it reloaded each and every time.
To achieve this, add such code:
$.ajaxSetup({
cache: true
});
This is taken from the documentation page.
You could create a helper function:
var getScript = (function() {
var loadedFiles = {};
return function(filename, callback) {
if(loadedFiles[filename]) {
callback();
} else {
$.getScript(filename, function() {
loadedFiles[filename] = true;
callback();
});
}
};
})();

Busy indicator race condition in Javascript

I have the following (javascript/jquery) code to show a busy indicator (after a delay) while an image is loading:
function imgUpdate(arg) {
var loaded = false;
$("#image").one("load", function(){
loaded = true;
$("#busyIndicator").hide();
});
setTimeout(function(){
if (!loaded) {
$("#busyIndicator").show();
}
}, 250);
$("#image")[0].src = arg;
}
Sometimes, the indicator comes up and stays up. How is this possible if the browser's javascript engine is single-threaded? (This is on Firefox 3, by the way.)
One note: this seems to happen when the image being loaded is already cached.
Another note: if I log to my firebug console, all of the lines in imgUpdate are executed, but a log message inside the onload handler never prints on subsequent calls to imgUpdate.
Is there any other javascript on the page that breaks? If so, this may not be a race condition -- JS could simply stop executing before the busyIndicator is hidden again...
I'm hard pressed to replicate this.
Here is the implementation of what you're doing:
A version using caching:
http://jsbin.com/uwuho
A version with caching being prevented: (uses parameter to avoid caching)
http://jsbin.com/oguvi
Hit F5/Ctrl-F5 to see it go. (in particular with the version which prevents caching)
With or without caching neither version is doing what you'd described.
Your problem probably lies elsewhere.
Clearing the image's src tag seems to fix the problem:
function imgUpdate(arg) {
var loaded = false;
$("#image").one("load", function(){
loaded = true;
$("#busyIndicator").hide();
});
setTimeout(function(){
if (!loaded) {
$("#busyIndicator").show();
}
}, 250);
$("#image")[0].src = "";
$("#image")[0].src = arg;
}
You might want to clear the timeout in your callback so that it won't fire if the image is loaded.
var timer = null;
function imgUpdate(arg) {
var loaded = false;
timer = setTimeout(function(){
$("#busyIndicator").show();
timer = null;
}, 250);
$("#image").one("load", function(){
if (timer) {
clearTimeout(timer);
timer = null;
}
$("#busyIndicator").hide();
});
$("#image")[0].src = arg;
}

Categories