Element within AJAX-fetched HTML cannot by found by getElementbyID - ajax

It's because it hasn't initialized yet. If I put in an alert(), it allows the browser to be freed up and initialize the element. The question, then, is how can I force it to initialize the element without using an alert box?
Here's the pertinent code...
$().ready(function() {
AJAX_LoadResponseIntoElement ("mybody", "skin1.txt");
AJAX_LoadResponseIntoElement ("contentdiv", "index.txt");
initPage();
});
AJAX_LoadResponseIntoElement (id, file) simply fetches "file" with an XMLHTTPRequest and loads it into id's innerHTML.
initPage() works until it calls setContentHeight(), which works up until this point:
if (DOMheight > y_lbound) { document.getElementById('containerdiv').style.height = (DOMheight+container_ymod) + 'px'; }
If I put alert(document.getElementById('containerdiv')); prior to this line, it says that it's NULL, even though the "containerdiv" element should have been loaded with the very first call to AJAX_LoadResponseIntoElement.
If I put TWO copies of alert(document.getElementById('containerdiv')); prior to this line, the first one says NULL, and the second says "[Object HTMLDivElement]".
Clearly, then, it is just a problem of "containerdiv" not being initialized.
So, once again, the question is how can I force the initialization of these elements after being fetched by the XMLHTTPRequest, without using an alert()?

It seems that AJAX_LoadResponseIntoElement() is asynchronous, since it uses XMLHTTPRequest internally. One way to solve your problem would be to modify that function so it takes a callback function argument and calls it when the request succeeds:
function AJAX_LoadResponseIntoElement(elementId, fileName, callback)
{
// Issue XMLHTTPRequest and call 'callback' on success.
}
Then use the modified function like this:
$(document).ready(function() {
AJAX_LoadResponseIntoElement("mybody", "skin1.txt", function() {
AJAX_LoadResponseIntoElement("contentdiv", "index.txt", initPage);
});
});

Related

Polymer async not working in FireFox

Has anyone run into problems with polymer's 'async' call in FireFox? My site is working well in the latest version of Chrome, but not so much luck in FireFox. One of the first things the site endeavors to do is to retrieve some object collections from a database (via core-ajax / REST API). Immediately after calling the functions required to retrieve the collections, I make a call to a function that asynchronously populates local storage variables with the resulting collections from the various datasource elements that make the ajax calls and handle the responses. FireFox is calling the populateLocalStorage function specified as an argument to the async call, but unlike in Chrome, the call is occurring before the datasource elements have received their ajax responses and handled them accordingly. Therefore, '_bugs' and '_snakes' (in this example), end up as undefined. Here is the script excerpt from the polymer element in question:
<script>
Polymer('my-app-index', {
bugs: null,
snakes: null,
spiders: null,
ready: function () {
this.retrieveCollections();
},
retrieveCollections: function() {
// custom datasource elements that make necessary ajax call(s)
// (using core-ajax), and handle ajax responses appropriately
// This element accesses the resulting collections via 2 way databinding
// to the datasource(s) element attributes
this.$.bugsDS.getAll();
this.$.snakesDS.getAll();
this.$.spidersDS.getAll();
this.async(this.populateLocalStorage); // Populate local storage with resulting collections
},
populateLocalStorage: function () {
mynamespace._bugs = this.bugs;
mynamespace._snakes = this.snakes;
}
});
</script>
If each getAll() performs an async ajax request, it's extremely unlikely all won't finish in a single rAF (16.66ms). That's what this.async() does. It callback gets called one requestAnimationFrame later.
Instead you'll want to react to the arrays changing. Whenever they populate their respective arrays, you can set the local storage:
retrieveCollections: function() {
this.$.bugsDS.getAll();
this.$.snakesDS.getAll();
this.$.spidersDS.getAll();
},
bugsChanged: function() {
this.populateLocalStorage('_bugs', this.bugs);
},
snakesChanged: function() {
this.populateLocalStorage('_snakes', this.snakes);
},
populateLocalStorage: function (key, data) {
mynamespace[key] = data;
}
Alternatively, you could setup an observe block and handle both properties changing with a single function:
observe: {
'bugs snakes': 'populateLocalStorage'
}

jQuery Mobile 1.4+ pagecontainerbeforechange bug?

I am experimenting with the new way of handling page events in jqM and have run into a curious issue. When handling the pagecontainerbeforechange event
$(document).on('pagecontainerbeforechange',function(e,u){test(e,u,'changing');})
function test(e,u,msg){console.log($(u.toPage));}
Attempting to put a jQuery object wrapper around u.toPage - as done above - produces strange behavior.
Check out this fiddle to see what I mean
Click on the Second Page button and then view the console. Nothing will happen (the second page is not shown) and you will see a message along the lines of *Uncaught error:syntax error, unrecognized expression http://jsfiddle.net/egn7g5xb/1/show/#second
Now comment out Line 7 and run the fiddle again. No such issue this time and the second page gets shown.
Perhaps someone here might be able to explain what is going on here?
On initial run, jQuery Mobile creates a fake page before navigating to first page in DOM. At that stage, pagecontainerbeforechange fires twice and returns .toPage as an object.
Later on, upon navigating to other pages, it fires twice again; however, it returns a string first time (URL/hash) and second time it returns an object which is the page itself.
Therefore, when using that event, you have to determine whether .toPage is an object or a string.
$(document).on("pagecontainerbeforechange", function (e, data) {
if (typeof data.toPage == "string") {
/* parse url */
}
if (typeof data.toPage == "object") {
/* manipulate page navigating to */
}
});
Note that pagecontainerbeforetransition is similar to beforechange, however, it fires once and returns .toPage as an object.
First, create your pagecontainer events within $(document).on("pagecreate", "#first", function(){ .. }).
Then the selector for these events should be $(":mobile-pagecontainer") or $("body") NOT $(document).
function test(e,u,msg)
{
console.log(msg);
var IsJQ = u.toPage instanceof $;
console.log(IsJQ);
if (IsJQ){
console.log(u.toPage.data());
} else {
console.log(u.toPage);
}
console.log('---');
}
$(document).on("pagecreate", "#first", function(){
$(":mobile-pagecontainer").on('pagecontainerbeforechange', function (e, u) {
test(e,u,'changing');
});
$(":mobile-pagecontainer").on('pagecontainerchange',function(e,u){
test(e,u,'changed');
});
});
Updated FIDDLE

jQuery unable select element from getJSON

I'm using the .each method with the .getJSON method to print out objects in a JSON file. This works fine, however I am unable to add a click function to an element that has been printed out. I am trying to bind a function to the div with 'click' ID.
var loadData = function () {
$.getJSON("profiles2.json", function (data) {
var html = [];
html.push("<div id='click'>Click here</div>");
$.each(data.profiles, function (firstIndex, firstLevel) {
html.push("<h2>" + firstLevel.profileGroup + "</h2>");
});
$("#data").html(html.join(''));
});
};
$(document).ready(function () {
loadData();
$("#click").click(function () {
console.log('clicked');
});
});
$.getJSON() (like other Ajax methods) is asynchronous, so it returns immediately before the results have come back. So your loadData() method also returns immediately and you then try to bind a handler to an element not yet added.
Move the .click(...) binding into the callback of $.getJSON(), after adding the element(s), and it will work.
Alternatively, use a delegated event handler:
$("#data").on("click", "#click", function() {
console.log('clicked');
});
...which actually binds the handler to the parent element that does exist at the time. When a click occurs it then tests whether it was on an element that matched the selector in the second parameter.
And as an aside, don't bind click handlers to divs unless you don't care about people who are physically unable to (or simply choose not to) use a mouse or other pointing device. Use anchor elements (styled as you see fit) so that they're "click"-accessible via the keyboard and the mouse.
$.getJSON is an asynchronous call and probably hasn't finished by the time you are trying to bind to the element that it injects into your DOM. Put your binding inside the $.getJSON call after you append the element to the page at the bottom.

Event removal in Mootools, and syntax of event addition

So I have been adding my events thusly:
element.addEvent('click', function() {
alert('foobar');
});
However, when attempting to remove said event, this syntactically identical code (with "add" switched to "remove") does not work.
element.removeEvent('click', function() {
alert('foobar');
});
I assume this is because the two functions defined are not referenced the same, so the event is not technically removed. Alright, so I redefine the event addition and removal:
element.addEvent('click', alert('foobar'));
element.removeEvent('click', alert('foobar'));
Which works great, except now when the page loads, the click event is fired even before it's clicked!
The function is removed, though, which is great......
update: when you do .addEvent('type', function(){ }) and .removeEvent('type', function(){ }), even though the functions may have the same 'signatures', they are two separte anonymous functions, assigned on the fly. function 1 is !== to function 2 - hence there is no match when MooTools tries to remove it.
to be able to remove an exact handler, o:
function handler(){ ... }
el.addEvent('click', handler);
// .. later
el.removeEvent('click', handler);
Internally, events are actually a map of keys to functions in element storage. have a look at this fiddle i did a while back for another SO question - http://www.jsfiddle.net/mVJDr/
it will check to see how many events are stacked up for a particular event type on any given element (or all events).
similarly, removeEvent looks for a match in the events storage - have a look on http://jsfiddle.net/dimitar/wLuY3/1/. hence, using named functions like Nikolaus suggested allows you to remove them easily as it provides a match.
also, you can remove events via element.removeEvents("click") for all click events.
your page now alerts because you pass on alert as the function as well as execute it with the params 'foobar'. METHOD followed by () in javascript means RUN THE METHOD PRECEDING IT IMMEDIATELY, NOT LATER. when you bind functions to events, you pass the reference (the method name) only.
to avoid using an anonymous function and to pass argument,s you can do something like:
document.id('foobar').addEvent('click', alert.bind(this, 'foo'));
as bind raps it for you, but removing this will be even more complicated.
as for event delegation, it's:
parentEl.addEvents({
"click:relay(a.linkout)": function(e, el) {
},
"mouseover:relay(li.menu)": function(e, el) {
}
});
more on that here http://mootools.net/docs/more/Element/Element.Delegation#Element:removeEvent
keep in mind it's not great / very stable. works fine for click stuff, mouseenter is not to be used delegated, just mouseover - which means IE can fire mouseout when it should not. the way i understand it, it's coming improved in mootools 2.0
edit updating to show an example of bound and unbound method within a class pattern in mootools
http://www.jsfiddle.net/wmhgw/
var foo = new Class({
message: "hi",
toElement: function() {
return this.element = new Element("a", {
href: "http://www.google.com",
text: "google",
events: {
"click": this.bar.bind(this), // bind it
"mouseenter": this.bar // unbound -> this.element becomes this
}
});
},
bar: function(event) {
event.stop();
// hi when bound to class instance (this.message will exist)
// 'undefined' otherwise.
console.log(this.message || "undefined");
}
});
document.id(new foo()).inject(document.body);
the mouseenter here will be unbound where this will refer to the default scope (i.e the element that triggered the event - the a href). when bound, you can get the element via event.target instead - the event object is always passed on to the function as a parameter.
btw, this is a slightly less familiar use of class and element relation but it serves my purposes here to illustrate binding in the context of classes.
assig the function to a variable and use the same reference to add and remove the event.
if you use an anonymous function you will get to different references
var test = function(){ alert('test: ' + this.id); }
$('element').addEvent('click', test);
...
$('element').removeEvent('click', test);
addEvent : Attaches an event listener to a DOM element.
Example -
$('myElement').addEvent('click', function(){
alert('clicked!');
});
removeEvent : Works as Element.addEvent, but instead removes the specified event listener.
Example -
var destroy = function(){ alert('Boom: ' + this.id); } // this refers to the Element.
$('myElement').addEvent('click', destroy);
//later...
$('myElement').removeEvent('click', destroy);
This means when you add an event with a eventhandler not an anonymous function if you than remove the event than it will be removed.

Assign jQuery.get() to a variable?

What is the correct way of assigning to a variable the response from jQuery.get() ?
var data = jQuery.get("output.csv");
I was reading that jQuery.get() must have a callback function? why is that? and how would i use this callback function to assign the response back to the data variable?
Thanks in advance for your help and clarification.
Update:
Thank you all for your answers and explanations. I think i am starting to finally grasp what you are all saying.
My code below is doing the right thing only the first iteration of it.
The rest of the iterations its writing to the page undefined.
Am i missing something?
<tbody>
<table id="myTable">
<script type="text/javascript">
$.get('output.csv', function(data) {
csvFile = jQuery.csv()(data);
for ( var x = 0; x < csvFile.length; x++ ) {
str = "<tr>";
for ( var y = 0; y < csvFile.length; y++) {
str += "<td>" + csvFile[y][y] + "</td>";
}
str += "</tr>";
}
$('#myTable').append(str);
});
</script>
</tbody>
</table>
A callback function is required for asynchronous function calls, like an AJAX GET request. There is a delay between calling the get function and getting a response back, which could be a millisecond or several minutes, so you need to have a callback function that gets called when the asynchronous GET has completed its work.
Here's some more info on jQuery's AJAX get function: http://docs.jquery.com/Ajax/jQuery.get#urldatacallbacktype.
From jQuery's examples:
// this would call the get function and just
// move on, doing nothing with the results
$.get("test.php");
// this would return the results of the get
$.get("test.php", function(data){
alert("Data Loaded: " + data);
});
If you get undefined when you try to use the data variable in the callback function, open up the console in Firebug in Firefox and watch the get request. You can see the raw request and the response that it comes back with. You should get a better indication of the issue after seeing what's being sent to the server and what's being sent back to the client.
tsvanharen answered the question well, but DCrawmer's still missing the point. Let me attempt a clarification for him. I'm oversimplifying some of this, and smoothing over some details.
Look at the code shown below. This is pretty much the same code as tsvanharen's, except that I've replaced the anonymous function for the callback with an actual function pointer, and am a little more explicit so you can see what's going on:
var x = null;
function myCallback(data)
{
alert("Data Loaded:" + data);
}
$.get("test.php", myCallback);
// the rest of your code
alert("The value of X is: " + x);
Assuming that test.php takes even a moment or two to load, notice the order that the alerts probably come up in:
1. "The value of X is"
2. "Data Loaded"
The function $.get() runs instantaneously. JavaScript moves on and runs the rest of your code right away. In the background, it's retrieving your page at test.php. jQuery hides some of the messy details of this.
The callback function (the second argument to $.get()) runs later (asynchronously). Or, said another way, the function myCallback is a handler to an event. That event is "$.get() has finished retrieving the data". It doesn't get run until that point. It doesn't run when $.get() runs! $.get() just remembers where that function is for later.
The function myCallback may run milliseconds or minutes later, long after $.get() has been dealt with.
If myCallback doesn't run until minutes later, then what's the value of x when the "The value of X" code is run? It's still null. There's your bug.
To use the data retrieved from the page in your script, you have to do things more like this:
Start your script, declare your variable to hold the data.
Call $.get(), with a callback function to handle the return.
Do nothing else. [Or, at least, nothing that requires the data]
Let the page just sit there.
...sometime in the future...
X. Your callback function will get run, and have the results of your web page.
Your callback function can:
* Display the data
* Assign that data to a variable
* Call other functions
* Go along it's merry way.
Actually in your example the data will be the XMLHttpRequest request object.
var x;
$.get( 'output.csv', function(data){
x = data;
console.log(x); // will give you the contents.
});
I really struggled with getting the results of jQuery ajax into my variables at the "document.ready" stage of events.
jQuery's ajax would load into my variables when a user triggered an "onchange" event of a select box after the page had already loaded, but the data would not feed the variables when the page first loaded.
I tried many, many, many different methods, but in the end, the answer I needed was at this stackoverflow page: JQuery - Storing ajax response into global variable
Thanks to contributor Charles Guilbert, I am able to get data into my variables, even when my page first loads.
Here's an example of the working script:
jQuery.extend
(
{
getValues: function(url)
{
var result = null;
$.ajax(
{
url: url,
type: 'get',
dataType: 'html',
async: false,
cache: false,
success: function(data)
{
result = data;
}
});
return result;
}
}
);
// Option List 1, when "Cats" is selected elsewhere
optList1_Cats += $.getValues("/MyData.aspx?iListNum=1&sVal=cats");
// Option List 1, when "Dogs" is selected elsewhere
optList1_Dogs += $.getValues("/MyData.aspx?iListNum=1&sVal=dogs");
// Option List 2, when "Cats" is selected elsewhere
optList2_Cats += $.getValues("/MyData.aspx?iListNum=2&sVal=cats");
// Option List 2, when "Dogs" is selected elsewhere
optList2_Dogs += $.getValues("/MyData.aspx?iListNum=2&sVal=dogs");
You just need to specify the callback function in the parameter to get method. Your data will be in the variable you specify in the function.
$.get("output.csv", function(data) {
// Put your function code here, the 'data' variable will hold your data.
});

Resources