I have this simple code for get elements from a external source
for(var i=0; i<10; i++)
loadPage(link[i]);
function loadPage(href)
{
var ajax = new XMLHttpRequest();
ajax.open('get',href);
ajax.responseType = 'document';
ajax.onreadystatechange=function()
{
console.log(ajax.responseXML.querySelectorAll("a[href^='magnet']")[0].getAttribute("href"));
}
ajax.send();
}
but when i read the console i get only 2 or 3 result instead of 10. i think is because i can't run multiple onload. How i can fix this?
By onload, you mean multiple ajax.send? that is likely not the problem.
Could is simply be that the returned ajax isn't returning something that matches your selector (magnet) or that your server doesn't answer properly to all requests? replace your console.log with a simple console.log ("Here be dragons");
If you ajax fetch pages from the same server, you should check the logs see how many requests do you have, and if you always reply correctly. Some servers will return at 50x error when too many requests in parallel, or put a breakpoint in your loadPage function, the pause is going to be enough to let the server process everything in time.
Related
To start off this is what I am trying to accomplish:
I am trying to do file copies to an array of servers. There are several steps that must be completed in a specific order before and after these copies (for example, stopping IIS, backing up and clearing folders, running a bat file, etc) so they are not single operations.
To make this super easy I wrote an API in node.js that does simple tasks like copy files and folders, delete folders, etc. I then wrote a frontend in node.js using an express generator and Pug that uses javascript XMLHhttpRequests to send commands to the API depending on what I needed to do. I have the API written and running as well as the frontend. Now on to the problems:
If I have my XMLHttpRequest run in synchronous mode (example: xhttp.open("POST", url , false);) when the command is sent to the API to copy a folder if the folder takes several minutes to copy the browser freezes. Chrome displays a "Page Frozen" error. However, the job gets done correctly.
If I have my XMLHttpRequest run in asynchronous mode (example: xhttp.open("POST", url , true);) then every command gets sent to the API at once so that the fastest operation completes first and the commands are out of order. The copy will fail.
I've tried searching for a way to make it so that each operation sent from the frontend javascript has to return a SUCCESS (or 200 response) from the API before moving on to the next command but so far all I've seen is "just use synchronous". Right now that's what I'm doing. That doesn't seem like the best solution even though it works. Is there a better way to do this in a way that won't freeze the browser?
I figured this out by writing a function to handle the requests, setting a counter (for the steps of the process), and putting a switch statement in the if statement for the result. It wasn't exactly what I needed but the basics of my solution is in the answers to this question: How can I call ajax synchronously without my web page freezing
Here's what I did in case it helps anyone else who finds this question:
function myFunction (step, params, url) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
switch(step) {
case 2:
//url and params are set here, and step 2 is done here
myFunction(step, params, url);
break;
case 3:
// and so on and so forth
}
}
xhttp.open("POST", url , true);
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhttp.send(params);
step++;
}
//kick off the function
var step = 1;
var url = "my URL to the API with call";
var parameters = "my parameters";
myFunction(step, parameters, url);
I have the following piece of code:
for(var i=0;i<searchParamLength;i++)
{
(function(i){
url = "https://www.googleapis.com/customsearch/v1?q=" + searchParam[i] + "&cx=005894674626506192190:j1zrf-as6vg&key=AIzaSyCanPMUPsyt3mXQd2GOhMZgD4l472jcDNM&dateRestrict=" + date_length;
arrXhr[i] = new XMLHttpRequest();
arrXhr[i].open("GET", url, true);
arrXhr[i].onreadystatechange = function(){
var totalResults = 0;
if (arrXhr[i].readyState === 4 && arrXhr[i].status == 200)
{
totalResults = JSON.parse(arrXhr[i].responseText).searchInformation.totalResults;
console.log("Variable I: " + i + "Total results:" + totalResults);
totalResultsArr.push(totalResults);
}
};
arrXhr[i].send(null);
})(i);
}
Take a look inside my loop where I have a console.log(). I was experiencing some strange behavior in my code which is why I decided to check if the loop is behaving as expected. Indeed, take a look at the image below. The loop is executing in a strange way. It is first executing i=1 and then i=6 and then i=2, etc.
I have been researching on SO and I saw the following question but it didn't help me solve my problem. Anyone has any clue why is this happening?
The requests are asynchronous. There's no guarantee that they will complete in the order you've made them in - that's why you're not seeing contiguous values for i in the callback.
JS moves on after sending the request and the onreadystatechange even occurs when you get a response. The time taken to receive response may vary by the status of the connection, server processing time, server load etc. So the order of response is not necessarily be the order of request.
You can make a synchronous call alternatively. This is decided by the third argument of xhr.open
arrXhr[i].open("GET", url, false);
The procedure is slightly different in this case.
Here is a tutorial.
I am using a java script in php to redirect a URL. This works for me because each time the loop is run, the link opens in a new window and the desired action takes place. However I want to stop the script from opening new windows and run / load within the same page. If I open the link the same page, it redirects to the new page in the first run and the loop does not run further.
I have no control over the next pages as they are part of an API. I do not actually want to navigate to the new page(s) but I just want to run the http:// API, which may even run in the background or something.
Please advise on what would be the best method to handle this ?
This is my current script:
<?php
$x1=$_POST["textfield1"];
$msg = "New Message Alert From $x1. blah blah";
$query = "SELECT * FROM phone_list";
$result = mysql_query($query);
$num=mysql_numrows($result);
$i=0;
while ($i < $num)
{
$f1=mysql_result($result,$i,"phone");
echo'<script> window.open("http://linktonextpage&msg='.$msg.'&num='.$f1'"); </script>';
$i++;
}
?>
Thanks in Advance !
As you're discovering, once you move from one page to another at that point the previous page is gone from the browser. JavaScript won't continue to run from pages that are no longer loaded.
I do not actually want to navigate to the new page(s) but I just want to run the http:// API, which may even run in the background or something.
Then don't navigate to the page. Instead, make an AJAX request to the desired URL. Something as simple as this:
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
// This function is called when the AJAX request completes.
// Here you would check for errors or respond in some way, if needed.
};
xhttp.open('GET', 'http://someurl', true);
xhttp.send();
As long as you're not making too many concurrent requests, you can run that in a loop. Just enclose it in a function:
function sendRequest(url) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
// This function is called when the AJAX request completes.
// Here you would check for errors or respond in some way, if needed.
};
xhttp.open('GET', url, true);
xhttp.send();
}
And call that function in a loop over your urls:
for (var i = 0; i < myUrls.length; i++) {
sendRequest(url);
}
You can get a lot fancier with this pretty easily, passing the callback function into the sendRequest() function or having sendRequest() return the AJAX promise or lots of other things. It's up to you what further features you want to add. The main point here, in response to your statement above, is simply to make AJAX requests in the background rather than navigate the browser to the URLs.
Note: Currently your loop is server-side, which is emitting a lot of repeated code to the browser. You can probably just emit the JSON-encoded values and perform the loop client-side as demonstrated above. Or, if you really want to loop server-side, then just define a function like the one above and emit calls to that function in a loop. It's still a lot of repeated client-side code, but it'll get the job done.
Another solution - less correct than solution with AJAX,
but very simple:
You can add iframes instead of opening popups.
while(...){
echo '<iframe src="http://linktonextpage&msg='.$msg.'&num='.$f1'." width="100%" height="120"></iframe>';
}
In your code it seems like you want to send messages to all the numbers fetched from database so if you want to use JavaScript you can do as previous answer.
Another way of doing the same is without using JavaScript.
You can also make Curl request to the Api Urls. Within your PHP code without loading the page.
While (condition) { $service_url = 'http://linktonextpage&msg='.$msg.'&num='.$f1'';
$curl = curl_init($service_url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$curl_response = curl_exec($curl);
curl_close($curl);}
The following code produces nothing on the html page, it seems to break down on 'status':
var get_json_file = new XMLHttpRequest();
get_json_file.open("GET", "/Users/files/Documents/time.json", true);
**document.write(get_json_file.status);**
keep in mind, that I am on a Mac, so there is no C: drive....however, this line of code does work fine:
document.write(get_json_file.readyState);
I just want to know that I was able to successfully find my json file. Perhaps, I should ask, what should I be looking for to achieve what I want ?
Another basic question about AJAX. I suggest you to read the MDN article about using XMLHttpRequest. You can't access the 'status' property until it is ready, and you haven't even called the 'send()' method, which performs the actual request. You can't have a status without making an HTTP request first. Learn how AJAX works before trying to use it. Explaining it all would be too long and this is not the place.
You can only get the status when the ajax has finished. That is, when the page was loaded, or a 404 was returned.
Because you're trying to call status straight after the request was sent (or not sent, read the P.S), you're getting nothing.
You need to make an async call, to check that status only when the request finishes:
get_json_file.onreadystatechange = function (){
if (get_json_file.readyState==4 && get_json_file.status==200)
{
alert('success');
}
}
read more at http://www.w3schools.com/ajax/ajax_xmlhttprequest_onreadystatechange.asp
P.S as noted by #Oscar, you're missing the send().
If you want to try a synchronous approach, which would stop the code from running until a response is returned, you can try:
var get_json_file = new XMLHttpRequest();
get_json_file.open("GET", "/Users/files/Documents/time.json", false);
//notice we set async to false (developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)
get_json_file.send(); //will wait for a response
document.write(get_json_file.status);
//(Credit: orginial asker)
Because of security issues you are not allowed to send requests to files on the local system, but what you could do is look into the fileReader API. (more info here)
<sidenote>
The reason that the readyState works and not status is because by defualt
readyState has a value of 0 and status has no value so it would be undefined.
</sidenote>
in my ubuntu, the path will be prefixed with file:///
i think your json file path should have file:///Users/files/Documents/time.json, because mac and ubuntu based on unix
and then you can check ajax status using #TastySpaceApple answer
if you using google chrome, don't forget to launch it with -–allow-file-access-from-files command, because google chrome not load local file by default due to security reason
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).