I'm working on a little project in JavaScript/jQuery.
In order to display results from a calculation done in javascript, I'd like to open a new window with some predefined content and modify this content in order to display the results: Im using code like this:
var resultwindow = window.open('result.html')
var doc = $('body', resultwindow.document);
doc.append("<p>Result</p>")
This is not working since the result document is not yet loaded when I append the content, so it is overwritten with the contents of 'result.html'.
I also tried
$(resultwindow.document).ready(function() {
// ... Fill result document here
})
and
$(resultwindow.document).load(function() {
// ... Fill result document here
})
but ready() works only on the current document (it is called immediately, if the current document is already loaded), and load doesn't get called at all.
Perhaps someone can point me to the right direction. Thanks in advance!
EDIT:
I finally solved this by creating the new document "by hand" in Javascript like:
w = window.open('','newwinow','width=800,height=600,menubar=1,status=0,scrollbars=1,resizable=1);
d = w.document.open("text/html","replace");
d.writeln('<html><head>' +
'<link rel="stylesheet" type="text/css" href="style.cs"/></head>' +
+'<body></body></html>');
// use d to manipulate DOM of new document and display results
If I were to do the same thing today (two years of experience later), I'd use some Javascript template library like Handlebars to maintain a template and compile it to javscript.
Your load call doesn't work because you're attempting to handle the load of the document, and chances are document does not even exist at this point. Which means you are passing null into jQuery, and it gracefully ignores you. Handle the load event of the raw window reference instead, and then you should be good to go...
var win = window.open("result.html");
$(win).load(function() {
$("body").append("<p>Result</p>");
});
The problem you have is that load() doesn't do what you think it does.
Instead, use bind("load", function() { /* Your function here */ }); then everything should work.
Correction:
load() is actually a dual-use function -- if it's called with a function as its first parameter, then it binds it to the load event of the object (or objects) in question, otherwise it loads the returned data (if any) into the elements in question. See Josh's answer for the real reason why it's not working.
Send the data to result.html in the querystring and then have the result.html display the data from there. If you want to be less obvious about it going through you could hash the data in the querystring and have the result page dehash it.
Related
I have an Ajax call that returns a piece of html code that is supposed to replace old html code on the page, giving them new attributes. After I successfully dynamically change my elements, I want to run another piece of JS code that reads and uses some of the attributes of the dynamically reloaded elements. However, JS prefers to read the old data (as if it's running synchronously).
The only workaround I've found is to set a timer, but the timer's delay time has to be relatively high (300 ms) to guarantee that it's always done correctly. What is the right way to do this?
Here is a pseudo-code for what I have right now. It works but the 300ms delay time is terrible.
$.post( "ajax/test.html", function( newCode ) {
$("#myDynamicDiv").html(newCode);
setTimeout(function(){
//Use the data that was just stored in #myDynamicDiv
},300);
});
For me I use .promise().done() may be it'll work with you
$("#myDynamicDiv").html(newCode).promise().done(function(){
// your code here
});
Edit: To someone who'll comes here later ..While my code isn't working with Mohasen he find a solution himself .. Please find his answer below
I accepted Mohamed-Yousef's answer, but since that did not include the full answer, here is the full version of what I eventually did:
A JQuery ajax call always returns a "Deferred" object when it's called. You can use this object's "then()" method to run things after the ajax call is finished. Here is the code:
dfrd = $.post( "ajax/test.html", function( newCode ) {
$("#myDynamicDiv").html(newCode);
});
dfrd.then(function(){
//Anything that is here is guaranteed to happen after the Ajax call is done.
});
I have the follwing javascript code that it triggers an IronPython script when I load the report.
The only issue I have is that for a reason I don't know it does it (it triggers the script) a couple of times.
Can some one help me? below is the script:
var n=0;
$(function () {
function executeScript() {
if (n==0){
n=n+1;
now = new Date();
if (now.getTime()-$('#hiddenBtn input').val()>10000){
$('#hiddenBtn input').val(now.getTime());
$('#hiddenBtn input').focus();
$('#hiddenBtn input').blur();
}
}
}
$(document).ready(function(){executeScript()});
strong text});
Please, let me know if you need more information.
Thanks in advance!!!
I have had similar issues with Javascript executing multiple times. Spotfire seems to instance the JS more than once, and it can cause some interesting behavior...
the best solution, in my opinion, only works if users are accessing the document via a link (as opposed to browsing the library). pass a configuration block to set a document property with a current timestamp, which would execute your IP script. this is the most solid implementation.
otherwise, you can try something like this:
// get a reference to a container on the page with an ID "hidden"
var $hidden = $("#hiddenBtn input");
// only continue if the container is empty
if !($hidden.text()) {
var now = Date.now();
$hidden.text(now)
.focus()
.blur();
|}
this is essentially the same as the code you posted, but instead of relying on the var n, you're counting on the input #hiddenBtn > input being empty. there is a caveat that you'll have to ensure this field is empty before you save the document
one addtional solution is using a Data Function to update the document property, like #user1247722 shows in his answer here: https://stackoverflow.com/a/40712635/4419423
I'm trying to create an implementation of "infinite scrolling". Here is my relevant code:
var postsIndex = 0;
var chunk = 5;
function getNextChunk() {
for (i=0; i<chunk; i++) {
postsIndex++;
document.write(getTimeLineElement(posts[postsIndex]));
}
}
if (postsIndex === 0) { // initial chunk.
var contentDiv = document.createElement("DIV"); // Create the div container.
var divTextNode = document.createTextNode(getNextChunk()); // Create a text node with the initial chunk.
contentDiv.appendChild(divTextNode); // Append the text node to the div container.
contentDiv.style.border="solid";
}
$(window).scroll(function() {
if($(window).scrollTop() + $(window).height() > $(document).height() - 100) {
alert("near bottom!");
contentDiv.appendChild(getNextChunk());
}
});
The initial chunk loads just fine and the loading of the 2nd chunk is triggered just fine. However, when the 2nd chunk gets loaded the original content is lost leaving only what was appended. As you can see I have placed a border around the div for testing purposes. I'm not seeing that border. I'm not sure why. This may be an indication that somehow this whole thing is not in the div element like I think that it is. Can anyone please tell me what I'm missing? Thanks for any input.
... doug
Your function, getNextChunk, which is called at various times after the document has loaded, itself calls document.write. As described in the notes in MDN's document.write documentation, when called against a document that has loaded, document.write makes an implicit call to document.open. The Notes in the MDN documentation for document.open state that when called against an existing document, document.open clears that document.
As such, your function is wiping out the document before inserting your content.
I don't know what you expect any of this to do anyway...you're calling appendChild while passing in a function call. This means that function's return value would be passed in--but it doesn't return anything...and it calls document.write in a loop...it's really all over the map.
We will start with the line that is not behaving the way you expect.
contentDiv.appendChild(getNextChunk());
What does this mean?
It means, execute the getNextChunk function and whatever it returns, add it to the DOM as a child of contentDiv.
So, what does getNextChunk return?
function getNextChunk() {
for (i=0; i<chunk; i++) {
postsIndex++;
document.write(getTimeLineElement(posts[postsIndex]));
}
}
As far as I can tell from that code, it does not actually return anything. Therefore, you are adding nothing as a child of your content div.
So what is getting added to the DOM?
You are calling getTimeLineElement, but adding its return value (assuming it is a string) directly to the HTML content of the page. This does not go into the div, but just gets appended to the document.
What did I do wrong?
Returns: You are passing the return value of a function which does not return anything.
Side effects: You have a function named get*, which suggests it exclusively reads data, but actually mutates the page (called a side effect).
Data types: The result of a function named getTimeLineElement, which suggests it returns a DOM element, actually returns a string.
Always understand what data types you are working with and how they move around from function to function.
I've got a deceptively simple blog project in the works, where I'm trying to bring together Isotope Jquery (for layout/filtering/sorting), Infinite Scroll, and dynamic loading of all blog excerpts via Ajax (so filtering and sorting is applied to all excerpts before the user scrolls down the page (after which time they're loaded into the dom and then accessible)).
This question primarily deals with getting the blog post excerpt data via Ajax, to then be passed into Isotope filtering code. I'm not sure of the best way to do this, but am currently trying to loop through each page (of blog posts excerpts) with an ajax request and then access the data as one whole.
I've come across two different methods to loop through the ajax requests, each using then when jquery statements. The first is using the method give in this SO answer, the other is simply putting the entire then when statement inside of an $.each statement.
Method 1:
var pageCount = 15;
var pageCountArray = [];
for (var i = 1; i != pageCount; ++i) pageCountArray.push(i);
var deferreds = [];
$(pageCountArray).each(function() {
var pageNumber = this;
deferreds.push(
$.get('/page/' + pageNumber)
)
$.when.apply($, deferreds)
.then(function(data){
console.log(data);
// this outputs data as a string from the first page, then a list of objects
console.log(typeof(data));
// string
// 13 - object
});
});
Slight aside: Any ideas as to why this is outputting one string and then objects?
Method 2:
var pageCount = 15;
var pageCountArray = [];
for (var i = 1; i != pageCount; ++i) pageCountArray.push(i);
$(pageCountArray).each(function(data) {
var pageNumber = this;
$.when(
$.get('/page/' + pageNumber)
).then(function() {
console.log(data);
// this outputs 14 strings of data
console.log(typeof(data));
// 14 - string
})
});
I haven't yet figured out how to incorporate the Ajaxed data into my Isotope filter function, but I think I'll need to parse this into HTML first. Still getting my footing with javascript... in this case is one of these data types (objects vs strings) easier to parse into HTML? I suppose that's the key to my answer?
Much obliged for insights.
PS: Bonus points for anyone who might know of a better way to achieve this in a different way that somehow dovetails into Isotope/Infinite Scroll nicely (perhaps in a way that's more intended to play nice with these plugins... I've been unsuccessful in my searching).
PPS: The second method feels much cleaner... anyone know of a reason that it's not a good approach (using when then inside of an .each loop)?
Wow, this is a largely scoped question no wonder there aren't any responses. This is a massive question so I will do my very best to help. I have created many sites that include the sort/filtering of Isotope while using AJAX preload's with infinite scrolling so here is one of the simplest examples I have already written out...
First I must mention that this whole thing works much better with David DeSandro's ImagesLoaded plugin. This is mostly because it allows you to place a callback function (function to be executed once an event occurs) attached to the loading event of the final image in a given container. Wow that was wordy. How to put that better... It basically asks the container, are you done loading yet? No? How about now? You're loaded? Ok please do this function now then...
With that being implemented I would start with this code in my onLoad event like so...
$(function() {
extendJQ_PreLoad(); //I Will Get To This Function In A Min
//Use ImagesLoaded Plugin To Control Load Time Sync
$(container).imagesLoaded(function() {
cont.isotope({
itemSelector: ".box", //This is the class I use on all my images to sort
layoutMode: "masonry",
isOriginLeft: true,
isFitWidth: true,
filter: "*",
masonry: {
columnWidth: ".box"
}
});
preLoadNextImgSet(); //I Will Get To This Function In A Min
});
});
Ok so let's break this down. The ImagesLoaded plugin stops the Isotope plugin instantiation from happening before there are images present to sort/filter/load and/or handle. This is step 1. Step 2 would be to then start looking at the actual isotope plugin instantiation. I am telling it to use Masonry plugin as its layout style and then I am passing in an object literal with options under the array key 'masonry'. The array key here that is named masonry is the same as any instantiation you would have normally done in the past with the stand alone Masonry plugin (non-isotope or isotope-2).
Step 3 to look at here would be my beginning call to extendJQ_PreLoad();. This function is the function I wrote to let JQuery know that I need to extend it's core functionality in order to capacitate preloading any images I give it, as an array. Like so...
function extendJQ_PreLoad() {
$.preloadImages = function(args) {
for (var i = 0; i < args.length; i++) {
$("<img />").attr("src", args[i]);
}
}
} //end function
This is just a simple iterator and nothing fancy, it allows the images to be preloaded by using a neat trick associated with the DOM. If you load images in this way it loads then into memory but not into the DOM meaning it is loaded and hidden. Once you then insert this image anywhere it will insert very quickly as it is now loaded in cache and awaiting placement. You can view more about this here.
Finally the last to look at is my call to my preload function. This is a very simple call to a php file that simply goes and looks for the next set of images in order, if there is any to find. If it gets some images then it begins adding it to a temporary div in memory (again not on the DOM to be seen) and is now setup for simple DOM traversal. Let's view the function to dissect its functionality...
function preLoadNextImgSet() {
$.post('AjaxController/ajaxPreload_Gallery.php', {currStart: start, currSize: loadSize}, function(data) {
if(data!="") {
var y = $(document.createElement("div")).append(data).find("a"),
found = [];
y.each(function() {
found[found.length] = "img/gallery/" + $(this).text();
});
$.preloadImages(found);
}
});
} //end function
In this example I have two global variables living in my browser window from JavaScript that I would have declared. A start and a loadSize variable. The start variable represents the current place in our list of images that we currently are at and the loadSize variable sets a limit on how many images to preload each time.
Now that the variables are set and sent in to the PHP file via the $.post function, we can use the PHP file to find the appropriate images in order and have them loaded into memory awaiting usage. Whatever is returned here to the y variable gets iterated over by the each function and then preloaded. Once this functions scope is exited the imaginary div will be deleted and sent to garbage as it is not used simple iterated over.
Ok, now. Its been a journey but we are almost ready to begin the final method here. Let's first go back and look at what the first imagesLoaded call was doing now that we know the new functionality added in these functions. The imagesLoaded call in the DOM-Ready event has a call in its very bottom piece that preloads the images.... why? This is because once the page loads and the initial images are loaded into the isotope container, we need the page to now use this idle time to begin already loading the next set. So in other words once the images are placed and sorted and happy to just sit there, the next loadSize amount of images will be loaded and waiting for you to place them.
Now for the final function. This function is a generic function thats sole purpose is to load in the current preloaded images into the DOM officially and then to ask for the next set to be loaded. However what on earth would be calling this function? This is where the lazyloading or infinitescroll becomes useful to us. Somewhere in your page you need to add this function in...
$(window).scroll(function(){
scrollTop = $(window).scrollTop(),
windowHeight = $(window).height(),
docuHeight = $(document).height();
//AJAX Data Pull
if(((scrollTop + windowHeight)+35) >= docuHeight){
getNextImages();
}
});
This function is the magic function that allows the infinitescroll effect to occur. I have added 35 pixels or so of padding (the +35 randomly in my code) because sometimes you want it to load close to the end of the page but not quite the actual end of the page.
Ok so now that this is setup when we reach the end of the page this function will want to get all of the next images generically like we had mentioned. The function of mine looks like this...
function getNextImages() {
cont = $(container);
$.post('AjaxController/ajaxPortfolio_Gallery.php', {currStart: start, currSize: loadSize}, function(data) {
if(data!="") {
//Append New Photos Inside <a> Element Tag
var y = $(document.createElement("div")).append(data).find("a");
cont.append(y);
//Fix Image Layouts
cont.imagesLoaded(function() {
//Feed Isotope Layout The New Items
cont.isotope("appended", y);
cont.find("a").css({"opacity":"1"});
});
} else { unFilled = false; }
});
}
I have included the unFilled variable simply so that there is a flag that can be set when you have reached the end of the images. You don't want it to keep trying to load forever if there are no images left to show.
Ok, so. This is a lot of information so I will try to keep answering as much as possible.
so i have a .js file that 2 different jsp pages call.
.js file contains:
var savedObj;
function A(obj){ savedObj = obj);
function B(){ alert(savedObj);
X.jsp file calls function A such that a DOM element onchange = functionA(this);
Y.jsp file calls function B such that body onload = function B
For some reason, my debugging in function A shows that the assignment of savedObj = obj worked correctly, but in function B, savedObj printed out null.
Thanks guys
This is happening because your function B is being called when the body of the JSP has finished loading. This will happen before any change event on a specific DOM element.
If you need function B to have the updated savedObj, you will need to wait to call it until after the change event is fired on your DOM element.
Note: this assumes that your JSPs are being included in the same rendered page, if they are not, this is happening because JavaScript state is not persisted from page to page
Using a cookie to store the value is certainly the best way to make the value persist across various pages. If you don't want to go into cookies, you could use javascript to write the new page in place of the current page and keep the value, but it's messy and you're better off taking the time to learn cookies.