I am appending an array to a div using jquery, I want to show a small loading indicator while this happens, so I threw a call back function onto my append logic. This works, however, my loader will fire off it's .hide() before my array is painted into the div.
Is there a way I can only run the .hide() after the array has finished painting?
var contentpush = [];
// create x-amount of divs (x being 'tabs')
for (var i = 0; i < tabs; i++){
contentpush.push('<li class="tab" role="tab"><span class="icon"><span class="'+contentformat[i].icon+'" ></span></span><span class="title">'+contentformat[i].title+'</span></li>');
}
$('.tablist').append(contentpush.join('')).ready(function(){$('#skeleton').hide(); });;
Here is a gif of what is going on as well:
As you can see, the skeletal frame loader will disappear long before my array is actually painted into the div. Any thoughts on this are greatly appreciated!
Have you tried setting a timeout for some milliseconds?
Unfortunately, the jQuery .append() function does not include a callback. There is no way to really check for completion of it, as it supposedly happens immediately. synchronously in the order they come, unless you explicitly tell them to be asynchronous by using a timeout or interval. see this
This means that your .append method will be executed and nothing else will execute until that method have finished its job. try following
// set a time interval for the event to complete.
$(contentpush).hide().appendTo(".tablist").fadeIn(1000);
// a small tweak to set a call back function.
$(".tablist").append(contentpush).append(function() { $('#skeleton').hide(); });
Related
I need to add an HTML5 video to a page with the following code:
document.querySelector("#mydiv").appendChild(video);
console.debug(document.querySelector("video").clientWidth);
My problem is, the second line returns 0 because it takes a while for the video tag to load the video it's going to play. If I manually type the second line in the console once the page is loaded, I get the actual value. Is there a better way to get this value than using timeouts to periodically check if the value's been updated?
I needed to do something similar and stumbled upon this question, as I though maybe there is a callback for this. Turns out there is not. So here is another way of doing it using Window.requestAnimationFrame().
It would look something like this:
// Prepare the new element
let video = document.createElement('video');
// Add the element to the DOM. Works with any element
document.querySelector("#mydiv").appendChild(video);
// Updateing the DOM will cause a re-render, which we can hook into
window.requestAnimationFrame(() => {
console.debug(document.querySelector("video").clientWidth);
});
<div id="mydiv">Video goes here:</div>
Check the console to see that it works.
Javascript is one-threaded.
You should to drop your code to event-loop, this allows the js with DOM work.
Just try followed:
document.querySelector("#mydiv").appendChild(video);
setTimeout(function(){
console.debug(document.querySelector("video").clientWidth);
});
Add a video.onloadstart event to the element.
video.onloadstart(getClientWidth);
document.querySelector("#mydiv").appendChild(video);
function getClientWidth() {
console.debug(document.querySelector("video").clientWidth);
}
Read more here.
I'm using the Boxer library from www.formstone.it to display a modal popup window over my HTML page. On triggering the modal window, HTML content gets loaded into the modal DIV from a file on my server. The Boxer code:
$(".boxer.boxer_object").click(function(e) {
e.preventDefault();
e.stopPropagation();
$obj = $('<div class="outer_container" />').load("http://www.myserver.com/game_modal.html",function(){
setTimeout(function (){
... Kinetic code which loads several image and GUI elements for a simple game ...
}, 2000); // delay Kinetic code execution with 2 seconds
$.boxer($obj);
});
Even though it does seemingly only execute the KineticJS code AFTER the HTML code has loaded, I do still sporadically get the following error:
Uncaught TypeError: Cannot set property 'innerHTML' of null
As I understand it, this error occurs when the canvas is trying to target a DIV which does not yet exist. In other words, the KineticJS code executes AFTER the code has loaded but BEFORE the relevant container DIV has become part of the page structure.
As seen in my code above, I now use a setTimeout() function to delay the KineticJS code execution with 2 seconds. Even though less than ideal, I have not seen the error again, with the game graphics loading every time. However, this is a fix that may be working on my browser but which may fail for someone else in other conditions.
Is there a proper way in which to ensure that the KineticJS code will ALWAYS execute AFTER the externally loaded HTML code has become part of the page structure? i.e. after the container DIV which the KineticJS code targets for the HTML5 canvas actually exists?
You should be able to skip the ajax call and render the game's container and loading markup manually:
var $game = $('<div id="outside_container" style="text-align:center; width:900px; height=600px;"><span style="display:inline-block; line-height:600px; font-size: 4em;">LOADING...</span></div>');
Then use the 'callback' option to initialize the game:
$boxer($game, {
callback: initGame
});
function initGame() {
// kinetic js code
...
}
Disclaimer: I haven't used boxer.
I took a quick peek at your boxer link.
There is a callback which executes "after opening instance".
How about putting your Kinetic code in that callback function you can supply to boxer.
From what I can understand, you are trying to create a div with the class of 'outer_container' when the onclick event occurs. You then want to 'load' your game modal from your web service and then run the kinetic js and boxer code asynchronously (via the callback) when it has been returned.
Asynchronous functionality can always been a bit fiddly. In your asynchronous chain of events, I think creating the div at the same time as attaching a .load() function to it means that sometimes the web service may be ready before the div has been created.
Have you tried creating the divelement before calling a web service pointing at that element?
You could either create thediv when you first intialise the page or try this...
$(".boxer.boxer_object").click(function(e) {
e.preventDefault();
e.stopPropagation();
//create the div first before the web service call to ensure it exists...
var obj = document.createElement('div');
obj.setAttribute('class', 'outer_container');
$('.outer_container').load("http://www.myserver.com/game_modal.html",function(){
//your code here, called after the web service has returned data...
$.boxer(obj);
});
I would personally just declare the 'outer_container' div before the declaration of the onclick event.
I hope this helps :).
I have div with vertical scroll bar. Div is being updated dynamically via ajax and html is inserted using jQuery's .html method.
After div is updated scroll bar returns to top and I am trying to keep it in the previous position.
This is how I'm trying it:
var scrollPos = $('div#some_id').scrollTop(); //remember scroll pos
$.ajax({...
success: function(data) {
$('div#some_id').html(data.html_content); //insert html content
$('div#some_id').scrollTop(scrollPos); //restore scroll pos
}
});
This fails. My best guess is that it is failing due to inserted html not rendered (ie. no scroll).
For example this works.
setTimeout(function(){
$('div#some_id').scrollTop(scrollPos);
}, 200);
But this is dirty hack in my opinion. I have no way of knowing that some browsers won't take more then these 200ms to render inserted content.
Is there a way to wait for browser to finish rendering inserted html before continuing ?
It's still a hack, and there really is no callback available for when the HTML is actually inserted and ready, but you could check if the elements in html_content is inserted every 200ms to make sure they really are ready etc.
Check the last element in the HTML from the ajax call:
var timer = setInterval(function(){
if ($("#lastElementFromAjaxID").length) {
$('div#some_id').scrollTop(scrollPos);
clearInterval(timer);
}
}, 200);
For a more advanced option you could probably do something like this without the interval, and bind it to DOMnodeInserted, and check if the last element is inserted.
I will just like to point out one difference here: One thing, is when the .html() have completed loading, but the browser actually render the content is something different. If the loaded content is somewhat complex, like tables, divs, css styling, images, etc - the rendering will complete somewhat later than all the dom ellements are present on the page. To check if everything is there, does not mean the rendering is complete. I have been looking for an answer to this by myself, as now I use the setTimeout function.
Such callback does not exists because .html() always works synchronously
If you are waiting for images loading, there's one approach https://github.com/desandro/imagesloaded
I have the following problem: on a customer's homepage the navibar is loaded by javascript, but I need to change some URL's on it. If I just start my script on $(document).ready() it runs before the customers script and has no effect. I only can use setTimeout for my function to wait until the other script is ready, but it's not good or safe at all. I can't change anything on the website, only add a javascript - is there a way to time it after the other one?
You can use repeated setTimeout, in order to check if menu is accessible.
function check_menu(){
if(document.getElementById('my_menu')==null){
setTimeout('check_menu()',500);
} else {
//do some stuff
}
}
If you have information about the menu like the id or class, use the onLoad() jQuery method on the element. For example if the code is loading asynchronously, and you add the onload to one of the last elements it should fire after the content has finished.
$.post('AsyncCodeLoad.php', function(data) {
$('#lastElementToLoad').onLoad(RunMyFunction);
});
Or if you have no chance to insert your code into the async loading just add to the bottom of the </body>:
$('#lastElementToLoad').onLoad(RunMyFunction);
Just a thought.
Yes, add your script at the bottom of the <body /> tag to ensure it does not run until all other scripts have run. This will only work however if your customer is loading the nav links synchronously.
If the nav is being loaded asynchronously, use JS's setInterval to repeatedly check the contents of the nav for links. When you determine the links have been added, cancel your interval check and call your script's logic entry point.
Cheers
Fiddle: http://jsfiddle.net/iambriansreed/xSzjA/
JavaScript
var
menu_fix = function(){
var menu = $('#menu');
if(menu.length == 0) return;
clearInterval(menu_fix_int);
$('a', menu).text('Google Search').attr('href','http://google.com');
},
menu_fix_int = setInterval(menu_fix, 100);
HTML
<div id="menu">Bing Search</div>
After updating the DOM with a new element (e.i. body.append(html)), I cannot immediately get the height of the newly updated element (e.g. body.height()). A partial fix is setting a time interval and getting the height at the end of time interval (setTimeout("alert(body.height)", 500)).
Any better ideas? Like a callback after DOM completely updates? Thanks.
Edit:
This only happens when there is a lot going on in the background (i.e. when page first loads). It would seem like the page update is almost instantaneous if nothing else is being processed. What I need is a callback, or an indicator of when the DOM is reformed!
Edit:
The problem is from the browser being 'distracted' by other processes, which makes the browser unable to update immediately after the append of the element. How do I fix this?
The timeout method works because the rendering engine is given a chance to display the new element there by giving it a change to render it and thus assigning it a height.
You can set the timeout to 0 and still have the same effect.
With jQuery works fine for me.
In the jsfiddle demo I put the next code:
$(function(){
var jTest = $('#test');
console.log('The height:',jTest.innerHeight(),jTest.height()); //Show me 'The height: 20 20'
jTest.append('<div><br/>How are you?</div>');
console.log('The height:',jTest.innerHeight(),jTest.height()); //Show me 'The height: 60 60'
});
Unless you mean javascript only solution or put a jsFiddle demo to show your error.