In my web app, I need to send the latest data the user has changed before they leave the page.
I call up a function like this when the page unloads:
window.onbeforeunload=sendData;
That's what's inside the function called
function sendData(){
var xhr = new XMLHttpRequest;
var storage = container;
xhr.open("POST","save.php",false);
xhr.send("information="+container);
}
My questions:
What is more right: Using async or sync to make send the requests before the user closes the page?
Is it possible to make the requests smaller? I only send variables containing up to two characters and the whole request takes 171 bytes!
It's necessary to use a synchronous request, otherwise, the data is not transmitted in IE10 and IE11, see Unload event in IE10, no form data.
Related
Disclaimer
Firstly, a disclaimer: I am working within specific boundaries, so whilst it may seem I'm going about something the long way round, I am limited as to what I can do. I know I should be doing this entirely differently, but I cannot. If it's not possible to do what I'm trying to do here, then that's fine, I just need to know.
Background
Basically, this boils down to a cross-domain javascript call. However, I need to wait for the response before returning the method.
Say I have a page - example1.com/host.html. This contains a javascript method of 'ProvideValue()' which returns an int. Edit: This method must be executed where it is found, since it may need to access other resources within that domain, and access global variables set for the current session.
https://example1.com/host.html
function ProvideValue(){
return 8; // In reality, this will be a process that returns a value
}
This host.html page contains an iframe pointing to example2.com/content.html (note the different domain). This content.html page contains a method that needs to display the value from host.html in an alert.
https://example2.com/content.html
function DisplayValue(){
var hostValue = //[get value from ProvideValue() in host.html]
alert(hostValue);
}
That's it.
Limitations
I can run any javascript I like on the host.html, but nothing server-side. On content.html I can run javascript and anything server-side. I have no control over the example1.com domain, but full control over example2.com.
Question
How can I retrieve the value from ProvideValue() on example1.com/host.html within the DisplayValue() method on example2.com/content.html?
Previous Attempts
Now, I've tried many of the cross-domain techniques, but all of them (that I've found) use an asynchronous callback. That won't work in this case, because I need to make the request to the host.html, and receive the value back, all within the scope of a single method on the content.html.
The only solution I got working involved relying on asynchronous cross-domain scripting (using easyXDM), and a server-side list of requests/responses in example2.com. The DisplayValue() method made the request to host.html, then immediately made a synchronous post to the server. The server would then wait until it got notified of the response from the cross-domain callback. Whilst waiting, the callback would make another call to the server to store the response. It worked fine in FireFox and IE, but Chrome wouldn't execute the callback until DisplayValue() completed. If there is no way to address my initial question, and this option has promise, then I will pose this as a new question, but I don't want to clutter this question with multiple topics.
Use XMLHttpRequest with CORS to make synchronous cross-domain requests.
If the server doesn't support cors, use a proxy which adds the appropriate CORS headers, e.g. https://cors-anywhere.herokuapp.com/ (source code at https://github.com/Rob--W/cors-anywhere).
Example 1: Using synchronous XHR with CORS
function getProvidedValue() {
var url = 'http://example.com/';
var xhr = new XMLHttpRequest();
// third param = false = synchronous request
xhr.open('GET', 'https://cors-anywhere.herokuapp.com/' + url, false);
xhr.send();
var result = xhr.responseText;
// do something with response (text manipulation, *whatever*)
return result;
}
Example 2: Use postMessage
If it's important to calculate the values on the fly with session data, use postMessage to continuously update the state:
Top-level document (host.html):
<script src="host.js"></script>
<iframe name="content" src="https://other.example.com/content.html"></iframe>
host.js
(function() {
var cache = {
providedValue: null,
otherValue: ''
};
function sendUpdate() {
if (frames.content) { // "content" is the name of the iframe
frames.content.postMessage(cache, 'https://other.example.com');
}
}
function recalc() {
// Update values
cache.providedValue = provideValue();
cache.otherValue = getOtherValue();
// Send (updated) values to frame
sendUpdate();
}
// Listen for changes using events, pollers, WHATEVER
yourAPI.on('change', recalc);
window.addEventListener('message', function(event) {
if (event.origin !== 'https://other.example.com') return;
if (event.data === 'requestUpdate') sendUpdate();
});
})();
A script in content.html: content.js
var data = {}; // Global
var parentOrigin = 'https://host.example.com';
window.addEventListener('message', function(event) {
if (event.origin !== parentOrigin) return;
data = event.data;
});
parent.postMessage('requestUpdate', parentOrigin);
// To get the value:
function displayValue() {
var hostName = data.providedValue;
}
This snippet is merely a demonstration of the concept. If you want to apply the method, you probably want to split the login in the recalc function, such that the value is only recalculated on the update of that particular value (instead of recalculating everything on every update).
Is it possible to use JavaScript to scrape all the changes to a webpage that is being updated live with AJAX? The site I wish to scrape updates data using AJAX every second and I want to grab all the changes. This is a auction website and several objects can change whenever a user places a bid. When a bid is placed the the following change:
The current Bid Price
The current high bidder
The auction timer has time added back to it
I wish to grab this data using a Chrome extension built on JavaScript. Is there a AJAX listener for JavaScript that can accomplish this? A tool kit? I need some direction. Can JavaScript accomplish this??
I'm going to show two ways of solving the problem. Whichever method you pick, don't forget to read the bottom of my answer!
First, I present a simple method which only works if the page uses jQuery. The second method looks slightly more complex, but will also work on pages without jQuery.
The following examples shows how you can implement filters based on method (eg POST/GET), URL, and read (POST) data and response bodies.
Use a global ajax event in jQuery
More information about the jQuery method can be found in the documentation of .ajaxSuccess.
Usage:
jQuery.ajaxSuccess(function(event, xhr, ajaxOptions) {
/* Method */ ajaxOptions.type
/* URL */ ajaxOptions.url
/* Response body */ xhr.responseText
/* Request body */ ajaxOptions.data
});
Pure JavaScript way
When the website does not use jQuery for its AJAX requests, you have to modify the built-in XMLHttpRequest method. This requires more code...:
(function() {
var XHR = XMLHttpRequest.prototype;
// Remember references to original methods
var open = XHR.open;
var send = XHR.send;
// Overwrite native methods
// Collect data:
XHR.open = function(method, url) {
this._method = method;
this._url = url;
return open.apply(this, arguments);
};
// Implement "ajaxSuccess" functionality
XHR.send = function(postData) {
this.addEventListener('load', function() {
/* Method */ this._method
/* URL */ this._url
/* Response body */ this.responseText
/* Request body */ postData
});
return send.apply(this, arguments);
};
})();
Getting it to work in a Chrome extension
The previously shown code has to be run in the context of the page (in your case, an auction page). For this reason, a content script has to be used which injects (!) the script. Using this is not difficult, I refer to this answer for a detailled explanation plus examples of usage: Building a Chrome Extension - Inject code in a page using a Content script.
A general method
You can read the request body, request headers and response headers with the chrome.webRequest API. The headers can also be modified. It's however not (yet) possible to read, let alone modify the response body of a request. If you want this feature, star https://code.google.com/p/chromium/issues/detail?id=104058.
I wanted to know if there is a way to stop Javascript from calling a php every page and populating an array, and instead just carry the array accross all the pages the user browsers.
Currently every page load it makes a new reqest to the server and repopulates the array for example when a user clicks link on a html page.
This is what i have in my JS file:
//Browser Support Code
function call_data(url,data){
if (window.XMLHttpRequest) {
AJAX=new XMLHttpRequest();
} else {
AJAX=new ActiveXObject("Microsoft.XMLHTTP");
}
if (AJAX) {
querystring = "?dta="+data;
AJAX.open("GET", url + querystring, false);
AJAX.send(null);
return AJAX.responseText;
} else {
return false;
}
}
var statistics = JSON.parse(call_data('user_info.php',userid));//user data
I don't currently see an advantage if its calling every page load, as I might as well do without ... unless theres a way to keep my array set each page load?
You should use HTML5 Local Storage.
You could use the Web Storage / DOM Storage API through JavaScript. It has decent browser support and if you implement it properly you can always fall through to requesting the PHP page if Web Storage is not available.
Here is a tutorial to get you started:
http://www.diveintojavascript.com/tutorials/web-storage-tutorial-creating-an-address-book-application
You can store it in a cookie and check if the value in the cookie is valid, load it from cookie, otherwise request it from the server and save it in the cookie for future use.
I need to make sure that when someone reloads or closes my page, their progress is saved. To save progress, I do a POST via XMLHttpRequest(), sending the data to server.
I'm triggering this saveData() function inside a window.onbeforeunload event.
The problem is, that saveData() does some calculations, and then calls a sendData(content) to finally actually POST the data.
And if the data I'm processing&sending is large (>100kB), the window seems to close before all the data gets through to the server. (I'm sending an image, and some times I only get half of it on the other side)
The http request is synchronous, xhr.open("POST", url, false), but that doesn't seem to cut it.
So my question is, how can I keep the onbeforeunload function from terminating, UNTIL xhr.readyState == 4? How do I make it wait for that event?
Oh, and setTimeout() will not work. The window will close before the "Time" comes.
This won't work:
if (!jQuery) {
setTimeout(attachCode, 100);
return;
} else {
// continue and close
}
Thanks for any insight!
PS: this shouldn't be "bad practice" since it only takes a few of seconds and I don't want the user to have to save manually. Also, saving while he works (as in, before he closes window), would slow down the app so I can't do that.
[EDIT] As a temporary measure, I resorted to using this trick to auto save the data if the user decides to stay on the page. However, I'd really like to not need to use any alert messages. :(
Disallowing the browser to do it's normal behaviour is normally a bad idea. And with onbeforeunload you can't pause the unload, you can only let the user know, that he/she is leaving the page and then let them decide, whether or not to leave the page - and thus the data unsaved.
So i your case, i would suggest an draft autosave, like you see in Google documents + a warning when the user leaves the page with unsaved data.
Actually you can slow down the browser before unloading. The problem lies in how JS handles the ajax requests.
Quite a while ago I had to do a quick and dirty hack about almost the same thing - logging some stuff, before navigating. The only way I found to do it is to wait for the return value of the XHR request.
Even though it's synchronous, the execution of the code forks in background and doesn't actually block the browser. You have to get the return value to be able to halt the script to upload the data.
Mind that I used jQuery for this, but it applies to native JS (I think.. :))
/* some code above */
var request = {
async:false,
cache: false,
url: this.serviceURL,
type: 'POST',
data: {...},
success: function(data) {}
};
try {
var asd = $j.ajax(request); // do the request and wait
}
catch (e) {}
/* some code below */
I hope this helps.
I am trying to create a bookmarklet that, upon clicking, would request some information from the user (a url and a couple other fields in this case) and then send that data to a php page on my server and then display the result.
I would like to do an Ajax call for this so that I don't actually redirect to the new page, just get the data but I assume I would run into the "Same Origin Policy" limitation of Ajax.... is there any known way of basically doing the same thing?
Also, what would be the best way to pass the parameters? I already have a mechanism in place to recieve the parameters as a post message from a form...is there any way I could just reuse this?
You can set a bookmarklet by create a bookmark and add that piece of code below in location, but, according to same origin policy limitation, that will only work when the current tab is on the same location, here www.google.com.
If I've understand well your needs, that should be ok for your problem.
var request = new XMLHttpRequest();
request.open("GET", "http://www.google.com", true);
request.onreadystatechange = function() {
var done = 4, ok = 200;
if (request.readyState == done && request.status == ok) {
if (request.responseText) {
alert(request.responseText);
}
}
};
request.send(null);
I don't know if POST would work.
You won't be able to do a post, but a GET will work fine. If you're using something like jQuery, it will simply create a script tag with a src URL which would send the data you are looking to submit.
You will have to return JSON style data.
See: http://docs.jquery.com/Ajax/jQuery.getJSON
Alternatively, your bookmarklet could create an iframe on the page, and that could do you work of submitting the data (you could use post then) if you weren't looking to communicate between the iframe and the page itself, but instead just use user input to submit.