I have a script that does 2 main things in a sequence:
It pulls data from an XML file through a GET request, and it appends the result into the DOM.
I have this "MacyJS" script which basically puts the content within a nicely calculated grid (a bit like Masonry).
My issue is that the first part is taking quite some time (I don't mind, it's meant for my own private use) to get the result and append it to the DOM. So that the "MacyJS" already ran before, and thus not rendering properly the HTML in a nice grid.
You can see I tried to put a delay for MacyJS, but of course this is not precise and doesn't always work in time, since the GET/append sometimes takes more than 1.5s.
How can I automatically run "MacyJS" only once the DOM has fully been updated with the new HTML/content ?
Thanks a lot for your help.
Code below for reference:
$(document).ready(function(){
// THE FIRST SCRIPT
var x=20;
$.get("my xml file here", function(data) {
var $xml = $(data);
$xml.find("item").each(function(i, val) {
var $this = $(this),
item = {
description: $this.find("description").text(),
guid: $this.find("guid").text()
};
$('#grid').append($('<div class="parsed appear"><a target="_blank" href="' +item.guid +'">' +item.description +'</a></div>'));
return i<(x-1);
});
});
// THE SECOND SCRIPT
setTimeout(function(){
var macy = Macy({
container: '#grid',
margin: 20,
columns: 4,
});
}, 1500);
});
Related
If I am here asking it is because we are stuck on something that we do not know how to solve. I must admit, we already searched in StackOverflow and search engines about a solution.. but we didn't manage to implement it / solve the problem.
I am trying to create a JavaScript function that:
detects in my html page all the occurrences of an html tag: <alias>
replaces its content with the result of an Ajax call (sending the
content of the tag to the Ajax.php page) + localStorage management
at the end unwraps it from <alias> tag and leaves the content returned from ajax call
the only problem is that in both cases it skips some iterations.
We have made some researches and it seems that the "problem" is that Ajax is asynchronous, so it does not wait for the response before going on with the process. We even saw that "async: false" is not a good solution.
I leave the part of my script that is interested with some brief descriptions
// includes an icon in the page to display the correct change
function multilingual(msg,i) {
// code
}
// function to make an ajax call or a "cache call" if value is in localStorage for a variable
function sendRequest(o) {
console.log(o.variab+': running sendRequest function');
// check if value for that variable is stored and if stored for more than 1 hour
if(window.localStorage && window.localStorage.getItem(o.variab) && window.localStorage.getItem(o.variab+'_exp') > +new Date - 60*60*1000) {
console.log(o.variab+': value from localStorage');
// replace <alias> content with cached value
var cached = window.localStorage.getItem(o.variab);
elements[o.counter].innerHTML = cached;
// including icon for multilingual post
console.log(o.variab+': calling multilingual function');
multilingual(window.localStorage.getItem(o.variab),o.counter);
} else {
console.log(o.variab+': starting ajax call');
// not stored yet or older than a month
console.log('variable='+o.variab+'&api_key='+o.api_key+'&lang='+o.language);
$.ajax({
type: 'POST',
url: my_ajax_url,
data: 'variable='+o.variab+'&api_key='+o.api_key+'&lang='+o.language,
success: function(msg){
// ajax call, storing new value and expiration + replace <alias> inner html with new value
window.localStorage.setItem(o.variab, msg);
var content = window.localStorage.getItem(o.variab);
window.localStorage.setItem(o.variab+'_exp', +new Date);
console.log(o.variab+': replacement from ajax call');
elements[o.counter].innerHTML = content;
// including icon for multilingual post
console.log(o.variab+': calling multilingual function');
multilingual(msg,o.counter);
},
error: function(msg){
console.warn('an error occured during ajax call');
}
});
}
};
// loop for each <alias> element found
//initial settings
var elements = document.body.getElementsByTagName('alias'),
elem_n = elements.length,
counter = 0;
var i = 0;
for(; i < elem_n;i++) {
var flag = 0;
console.info('var i='+i+' - Now working on '+elements[i].innerHTML);
sendRequest({
variab : elements[i].innerHTML,
api_key : settings.api_key,
language : default_lang,
counter : i
});
$(elements[i]).contents().unwrap().parent();
console.log(elements[i].innerHTML+': wrap removed');
}
I hope that some of you may provide me some valid solutions and/or examples, because we are stuck on this problem :(
From our test, when the value is from cache, the 1st/3rd/5th ... values are replaced correctly
when the value is from ajax the 2nd/4th .. values are replaced
Thanks in advance for your help :)
Your elements array is a live NodeList. When you unwrap things in those <alias> tags, the element disappears from the list. So, you're looking at element 0, and you do the ajax call, and then you get rid of the <alias> tag around the contents. At that instant, element[0] becomes what used to be element[1]. However, your loop increments i, so you skip the new element[0].
There's no reason to use .getElementsByTagName() anyway; you're using jQuery, so use it consistently:
var elements = $("alias");
That'll give you a jQuery object that will (mostly) work like an array, so the rest of your code won't have to change much, if at all.
To solve issues like this in the past, I've done something like the code below, you actually send the target along with the function running the AJAX call, and don't use any global variables because those may change as the for loop runs. Try passing in everything you'll use in the parameters of the function, including the target like I've done:
function loadContent(target, info) {
//ajax call
//on success replace target with new data;
}
$('alias').each(function(){
loadContent($(this), info)
});
I have a function to load and place images into an empty div (it's some kind of scrolling menu done by me) and to place images one behind another I use append function. All is going well until the upload to the server. The order of the pictures seems to be failed like the append() is not placing them one behind another.
loadContent = function(i){
var tytul = "#phot" + i;
var photurl = "../photos/" + activegal + "/" + i + ".JPG";
$.ajax({
url:photurl,
type:'HEAD',
error: function()
{
},
success: function()
{
newid = "photo"+i;
var img = $("<img />").attr({
src: photurl,
width:"120",
height:"90",
id: newid,
'class': "photon"
});
$(img).appendTo("#scrollbar");
counter++;
//$.delay(2);
});
};
<div id="scrollbar">
</div>
it seems you are trying to do a get on the images in an ajax call through a loop counter and then add these image elements to the <div> element, however ajax calls being asynchronous , the appendTo() of image#1 might be called after appendTo of image#2 depending on when each of the ajax calls returns (which in turn might depend on the size of the image, network traffic etc.).
The only way to ensure the elements are in order of server checks is by making the ajax calls synchronous by setting:
async: false
I have a very large JSON object, which serves as a data source from which my page is created. Currently I am able to load the whole object, create DIVs from it and the display it. However when the JSON is very big it takes a lot of time and then suddenly the whole content is displayed. I would like to load each child separately, or display the loaded content after each node (appending).
The JSON has the following structure:
{
id:"0",
text:"",
children:[{
id:"0-0",
text:"",
children:[...]
},
{
id:"",
text:"0-1",
children:[...]
}]
}
When I load this object I call a function which creates the container based on the root node and then a recursive function which gets executed for every child.
function loadRoot(wholeJson){
var rootDiv = '<div id="frag-' + wholeJson.id + '"></div>';
return rootDiv;
}
function loadChildren(wholeJson){
rootDiv = $('#frag-' + wholeJson.id);
wholeJson.children.forEach(function(child){
loadChild(child);
});
}
function loadChild(node){
var newDiv = // create the div here..
node.children.forEach(function(child){
loadChild(child);
});
newDiv += "</div>" //close it
return newDiv;
}
This works, but as I mentioned it loads the whole content at once. How can I achieve the displaying of every child after it's created and not only at the end of script execution, please?
I had a similar problem. The reason why this happens is that javascript first processes your function in the background, and when it is all done, it's changes will be made. I do not know if its the best way to go. But setTimeout with a anonymous function worked for me. Just set the waiting time really low.
I am able to execute external JS file (which is used to render chemical molecules) inside jQuery. However, when the external JS file is loaded, it overwrites everything from my initial file.
How am i supposed to insert the external JS file into the appropriate DOM (in this case, the last available class with className "render") without overwriting the existing HTML file?
I use an URL to get data in JSON format and parse it using .each() function as mentioned below.
$(document).ready(function(){
$.get("someURL", function (json){
var obj = $.parseJSON(json);
$(".container").append("<ul class =\"items\">");
$.each(obj, function(i){
name = obj[i].name;
mol = obj[i].mol;
script = $(document.createElement('script')).attr('type', 'text/javascript').attr('src','js/external.js');
$('ul.items').append('<li><div class="render">');
$(script).appendTo(".render:last");
});
});
});
My external js file is,
transform = new ChemDoodle.TransformCanvas(name, 150, 150, true);
transform.loadMolecule(ChemDoodle.readMOL(mol));
Also, please note that I have no programming experience and trying to learn JS/jQuery on my own for a couple of months.
Any help/guidance is much appreciated.
Thanks in advance,
Kaushik
I could solve the above problem.
The problem was in the external.js (chemdoodle JS which renders chemical compounds) file that I was using. It renders chemical compound structure inside . Due to an inherent problem in chemdoodle's JS component, it used to overwrite my file (irrespective of if I call it as external script or internal function). I had to explicitly insert DOM with the same id as the chemdoodle was using. Here is what I did and that solved the problem. It might help someone who is working with chemdoodle too!
$(document).ready(function(){
function canvasCall(name, mol){
transform = new ChemDoodle.TransformCanvas(name, 150, 150, true);
transform.specs.bonds_useJMOLColors = true;
transform.specs.set3DRepresentation('Stick');
transform.specs.atoms_display = false;
transform.specs.backgroundColor = 'black';
transform.specs.bonds_clearOverlaps_2D = true;
transform.loadMolecule(ChemDoodle.readMOL(mol));
};
$(".container").append("<ul class =\"items\">");
$.getJSON(someURL, function (obj){
$.each(obj, function(i){
var name = obj[i].name;
var mol = obj[i].mol;
$('ul.items').append("<li><div class=\"render\"><canvas id=\"" + name + "\">");
$("#"+name).append(canvasCall(name, mol));
});
});
});
I have been looking around for the simplest way to refresh a particular div on my page, automatically, every x seconds.
So far I've got:
<script type="text/javascript">
window.onload = startInterval;
function startInterval()
{
setInterval("startTime();",1000);
}
function startTime()
{
document.getElementById('time').innerHTML = Date();
}
However the last part where the innerHTML is set to the date function is what I'd like replaced by the content of the "time" ID, and not an actual date print.
I know I could load an iframe, or externa html page, but I would like to simply call upon an existing div on the page instead of having to change the existing content. Is that possible?
Thanks!
Edit: What I mean is I have a a div that looks like this on the page:
Some stuff
I would like to have that div refreshed every x seconds, so yes, you may ignore the Date() part of my example, I simply found that code as is but when I tried to remove the .innerHTML part it just does nothing, I think lol!
Are you looking for something like this?
<script type="text/javascript">
window.onload = startInterval;
function startInterval() {
setInterval("startTime();",1000);
}
function startTime() {
var now = new Date();
document.getElementById('time').innerHTML = now.getHours() + ":" + now.getMinutes() + ":" +now.getSeconds();
}
</script>
NOTE: The OP is actually wanting to reload a script in an ad service already included on the page. The following does not help with this; however, due to the way the question was asked, I'm leaving this answer since I think it could help others looking for the following type of solution. Just be aware this does not demonstrate how to "rerun" already included (presumably global and non-function'd) code.
Say I have the following div I'd like to dynamically refresh:
<div id="refresh">Refreshes...</div>
jQuery offers the $.ajax group of functions that allow you to dynamically request a page and use the response as HTML. For instance:
$(document).ready(function(){
var $refresh = $('#refresh'),
loaded = 1,
data = {
html: $.toJSON({
text: 'some text',
object: {}
}),
delay: 3
};
var refresh = function(){
$.ajax({
url: "/echo/html/",
data: data,
type: "GET",
success: function(response) {
$refresh.html($refresh.html() + '<br/>#' + loaded);
loaded++;
setTimeout(refresh, 3000);
}
});
};
refresh();
});
http://jsfiddle.net/Ah3jS/
Note, I'm using jsFiddle's echo/html/ functionality here as well. The data variable is tuned to working with this for demonstration purposes. In reality, the data sent with the request is either GET or POST variables like you work with normally. Also, I don't use response in success, but that's because it doesn't return anything in jsFiddle's demo mode.
jQuery make's this stuff pretty easy. Really, I'd think about using it instead of most other approaches (requirements notwithstanding).