AJAX - help me understand that sequence of code - ajax

function loadDoc() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4 && xhttp.status == 200) {
document.getElementById("demo").innerHTML = xhttp.responseText;
}
};
xhttp.open("GET", "ajax_info.txt", true);
xhttp.send();
}
Can someone please help me understand the sequence of execution here?
xhttp.onreadystatechange is called with an IF waiting for the State ==4. But it's not 4 until xhttp.send() is triggered further down in the code. So once xhttp.send() triggers State=4, why is then onreadystatechange called again to execute the IF? I guess I'm looking at this as a top-down execution. I just don't get how/why onreadystatechange "waits" for State to change? Since its code has already been executed. Please explain as simply as possible and thanks.

onreadystatechange is an event handler, which means it triggers when a specific event fires.
The XMLHttpRequest.onreadystatechange property contains the event
handler to be called when the readystatechange event is fired, that is
every time the readyState property of the XMLHttpRequest changes
(emphasis mine)

Related

Should you always set the AJAX request to TRUE for async?

Some minor question(just interested), in every AJAX request docs they set it to true(async).
Example:
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
//some code here
}
};
xhttp.open("GET", "ajax_info.txt", true);
xhttp.send();
But the JavaScrips is asynchronous language plus onreadystatechange already means that the request is async and here is the question, should you always specify "true" and why?P.S Maybe there is something minor happening behind the scenes?
Yes, you should. Synchronous AJAX blocks the UI and is deprecated, you should not use it.
Note that the default is true, so you can just leave that argument out. You only need to provide it explicitly if you have to use additional arguments after it.

Firefox Bootstrapped Add-On Injecting before load

Is it possible to add event listeners for a document before a page has been navigated to using a Bootstrapped add-on? I would like to see what page the user wants to navigate to as well as later after the page loads to inspect the DOM. I need to run code in the HTML content context.
In the past I used a toolbar XUL and included javascript within it and it would load before the HTML page loaded.
i looked into doing stuff before DOMContentLoaded sometime ago and found out there is a document inserted observer.
order of events after running research code at bottom
readystate changes to interactive (i think multiple times, not sure)
readystate changes to complete
DOMContentLoaded event fires
load event fires (Sometimes load doesnt fire, if you might have to change addEventListener with capture arugment (3rd argument) as false or true)
apparently there should be readystate loading before all of this but i can never catch it i dont know why.
after running the code in scratchpad, browser environemnt of course, then load a new page and watch the error console it will throw these reports in this order:
ready state changed! ("interactive") Scratchpad/4:18
02:28:07.873 ready state changed! ("complete") Scratchpad/4:18
02:28:07.874 DOMContentLoaded event fired! Scratchpad/4:53
02:28:07.938 Load event fired! Scratchpad/4:45
here is the research code. it adds a the listeners and observer to see whats firing.
var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
var os = Cc['#mozilla.org/observer-service;1'].getService(Ci.nsIObserverService);
var LMObserver;
function myObserver() {
this.register();
}
myObserver.prototype = {
observe: function (subject, topic, data) {
//Cu.reportError(subject);
//Cu.reportError(data);
//i think subject is window element
subject.onreadystatechange = function () {
//loading
//interactive
//complete
Cu.reportError('ready state changed! ("' + subject.readyState + '")');
//var body = subject.documentElement.querySelector('body')
//you want to change title so you would do that here do something like: if (subject.readystate == 'complete') { subject.title = 'blah blah' }
//Cu.reportError('has body element: ' + body)
}
},
register: function () {
os.addObserver(this, 'document-element-inserted', false);
},
unregister: function () {
os.removeObserver(this, 'document-element-inserted', false);
}
};
//below this is the DOMContentLoaded thing i put this here so we can see what fires in what order
var pageLoad = function(event) {
var win = event.originalTarget.defaultView;
if (win && win.frameElement) {
return;
}
Cu.reportError('Load event fired!');
}
var pageDOMContentLoaded = function(event) {
var win = event.originalTarget.defaultView;
if (win && win.frameElement) {
return;
}
Cu.reportError('DOMContentLoaded event fired!');
}
LMObserver = new myObserver;
gBrowser.addEventListener("load", pageLoad, true);
gBrowser.addEventListener("DOMContentLoaded", pageDOMContentLoaded, true);
//gBrowser.removeEventListener("load", pageLoad, true);
//gBrowser.removeEventListener("DOMContentLoaded", pageDOMContentLoaded, true);
//LMObserver.unregister();
Here's some more indepth research on load events added with true or false as capture argument: https://github.com/Noitidart/event-listener-experiment-DOMC-and-load/blob/master/bootstrap.js

Prototype.js event observe click intercept and stop propagation

I have a page that is built around a wrapper with some very defined logic. There is a Save button on the bottom of the wrapped form that looks like this:
<form>
... my page goes here...
<input id="submitBtnSaveId" type="button" onclick="submitPage('save', 'auto', event)" value="Save">
</form>
This cannot change...
Now, I'm writing some javascript into the page that gets loaded in "...my page goes here...". The code loads great and runs as expected. It does some work around the form elements and I've even injected some on-page validation. This is where I'm stuck. I'm trying to "intercept" the onclick and stop the page from calling "submitPage()" if the validation fails. I'm using prototype.js, so I've tried all variations and combinations like this:
document.observe("dom:loaded", function() {
Element.observe('submitBtnSaveId', 'click', function (e) {
console.log('Noticed a submit taking place... please make it stop!');
//validateForm(e);
Event.stop(e);
e.stopPropagation();
e.cancelBubble = true;
console.log(e);
alert('Stop the default submit!');
return false;
}, false);
});
Nothing stops the "submitPage()" from being called! The observe actually works and triggers the console message and shows the alert for a second. Then the "submitPage()" kicks in and everything goes bye-bye. I've removed the onclick attached to the button in Firebug, and my validation and alert all work as intended, so it leads me to think that the propagation isn't really being stopped for the onclick?
What am I missing?
So based on the fact that you can't change the HTML - here's an idea.
leave your current javascript as is to catch the click event - but add this to the dom:loaded event
$('submitBtnSaveId').writeAttribute('onclick',null);
this will remove the onclick attribute so hopefully the event wont be called
so your javascript will look like this
document.observe("dom:loaded", function() {
$('submitBtnSaveId').writeAttribute('onclick',null);
Element.observe('submitBtnSaveId', 'click', function (e) {
console.log('Noticed a submit taking place... please make it stop!');
//validateForm(e);
Event.stop(e);
e.stopPropagation();
e.cancelBubble = true;
console.log(e);
alert('Stop the default submit!');
return false;
submitPage('save', 'auto', e);
//run submitPage() if all is good
}, false);
});
I took the idea presented by Geek Num 88 and extended it to fully meet my need. I didn't know about the ability to overwrite the attribute, which was great! The problem continued to be that I needed to run submitPage() if all is good, and that method's parameters and call could be different per page. That ended up being trickier than just a simple call on success. Here's my final code:
document.observe("dom:loaded", function() {
var allButtons = $$('input[type=button]');
allButtons.each(function (oneButton) {
if (oneButton.value === 'Save') {
var originalSubmit = oneButton.readAttribute('onclick');
var originalMethod = getMethodName(originalSubmit);
var originalParameters = getMethodParameters(originalSubmit);
oneButton.writeAttribute('onclick', null);
Element.observe(oneButton, 'click', function (e) {
if (validateForm(e)) {
return window[originalMethod].apply(this, originalParameters || []);
}
}, false);
}
});
});
function getMethodName(theMethod) {
return theMethod.substring(0, theMethod.indexOf('('))
}
function getMethodParameters(theMethod) {
var parameterCommaDelimited = theMethod.substring(theMethod.indexOf('(') + 1, theMethod.indexOf(')'));
var parameterArray = parameterCommaDelimited.split(",");
var finalParamArray = [];
parameterArray.forEach(function(oneParam) {
finalParamArray.push(oneParam.trim().replace("'","", 'g'));
});
return finalParamArray;
}

Twitter Bootstrap carousel: event scope?

I have a Twitter bootstrap carousel .on(slid) event that is fired after clicking a element that targets a particular slide, and an .on(slid) event that is fired after clicking a different element (close button). However once the second event occurs, it continues to fire even with the first on(slid) event. I thought preventDefault() would fix this but it doesn't. I even tried a more complete function (shown below, from Herb Caudill) to do this, but no go.
Using (this) to tie the scope of the event to the function didn't work. I am fairly new at this, so I am thinking there may be a fundamental concept here that I'm not grasping.
Here's a simplified version of my code:
// Intended to prevent event bubble up or any usage after this is called.
eventCancel = function (e)
{
if (!e)
if (window.event) e = window.event;
else return;
if (e.cancelBubble != null) e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
if (e.preventDefault) e.preventDefault();
if (window.event) e.returnValue = false;
if (e.cancel != null) e.cancel = true;
}
$('.recentContent').click(function(){
var $lastIndex = $('#splashCarousel .carousel-inner').children().last().getIndex();
$('#splashCarousel').carousel($lastIndex); //slide to specific index
$('#splashCarousel').carousel('pause');
$('#splashCarousel').on('slid', function (e) {
alert("open");
//do stuff
return eventCancel(e);
})
});
$('.closeX').click(function(){
var $lastItem = $('#splashCarousel .carousel-inner').children().last();
$('#splashCarousel').carousel(0);
$('#splashCarousel').on('slid', function (e) {
alert("close");
//do other stuff
return eventCancel(e);
})
});
and here's an even simpler fiddle trying preventDefault: http://jsfiddle.net/chardwick/a5Dpe/
What might I being doing wrong?
Thanks!
Edit: I see some discussion elsewhere on SO about nesting events, both that they shouldn't be done, and that sometimes they have to. Either way I still seem to get multiple event firings. Using live() for event delegation looks promising. Still could use some feedback though, as it's all starting to muddle for me.

Two ajax function calls on single click

I've some folders in a div and contents of those folders are shown in tree view(when small plus button is clicked) using following code:
echo '<span class="toggle" onclick="getchildren(\''.$objectnode["fid"].'\', \'childdiv'.$objectnode["fid"].'\');" ></span>';
when folder is clicked,its contents are shown in another div,parallel to it using following code:
<span><?php echo $objectnode["object_name"]; ?></span>
Now what i want to do is, when i click on folder name,its contents should be load in div parallel to it as well as its child nodes should also be visible or expand at the same time. Any help will be appreciated
Just make two ajax calls. Ajax calls are asynchronous and you can make as many as you like. For example, you could do something like this:
function ajax(){
var xmlhttp = new XMLHttpRequest();
xmlhttp.open('POST', 'yourpage.php', true);
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4) {
// Your callback code goes here
xmlhttp.responseXML; // this is the response data
}
};
xmlhttp.send(yourdatahere);
var xmlhttp2 = new XMLHttpRequest();
xmlhttp2.open('POST', 'yourpage.php', true);
xmlhttp2.onreadystatechange = function() {
if (xmlhttp2.readyState == 4) {
// Your callback code goes here
xmlhttp2.responseXML; // this is the response data
}
};
xmlhttp2.send(yourdatahere);
}
Call this function from your onclick function and that should do the trick. You can also nest the function calls. For example, if you're waiting on data from the first call, put the second ajax call in the callback from the first and do all of the updating. You won't need to wait for the second ajax call to return to update the DOM.
You could also make a separate ajax call for each child node, and if you want to do that for all of the child nodes, you'll have to do some recursion, such as:
function ajax(parentNode){
var xmlhttp = new XMLHttpRequest();
xmlhttp.open('POST', 'yourpage.php', true);
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4) {
// Your callback code goes here
xmlhttp.responseXML; // this is the response data
// do stuff with responseXML
}
};
xmlhttp.send(yourdatahere);
var i;
for(i = 0; i < parentNode.childNodes.length; i++){
ajax(parentNode.childNodes[i]);
};
}
There are already plugins made for this. I know jQuery has one for it's framework. I built my own because it wasn't exactly what I wanted. I hope this helps!

Resources