I have a short snippet of Javascript which I want to poll a server every couple of seconds and update the DOM.
function updateCard() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
card = JSON.parse(this.responseText);
document.getElementById("season").innerHTML = card.season;
}
};
xhttp.open("GET", "/curr_card/", true);
xhttp.send();
}
window.onload = updateCard;
window.setInterval(updateCard,2000);
On most browsers that's what happens. There are a few one-off calls to updateCard, but on the whole the server shows ~1/2 connection per second per client.
However, when I access the page in Firefox on Android (49.0) the browser starts continuously polling /curr_card/, tens of times a second.
I've seen people suggest replacing the setInterval line with window.setInterval(function() {updateCard();},2000);, this doesn't help.
I'm pretty new to Javascript and AJAX, so have no idea why this is happening. Is it a bug in FF? I can post more code if requested.
Thanks in advance.
After testing and discussing in OP's comments, we concluded this must be an issue specific to Firefox on the OP's HTC M7, as it could not be reproduced on the same version Firefox on a Galaxy S7.
That may happen not only with Firefox on some device.
It may happen when response has not finished because of servers late answer but it sends another request and so on...
What if to do like this:
function updateCard(before, after) {
if(before) {
before();
}
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
card = JSON.parse(this.responseText);
document.getElementById("season").innerHTML = card.season;
}
if(after) {
after();
}
};
xhttp.open("GET", "/curr_card/", true);
xhttp.send();
}
window.onload = updateCard;
var updateCardRunning = false;
setInterval(function() {
if(updateCardRunning === true) {
console.log('postponing to next schedule');
return;
}
updateCard(
function() {updateCardRunning = true;},
function() {updateCardRunning = false;}
);
}, 2000);
or:
function updateCard() {
var xhttp = new XMLHttpRequest();
window.xhttp = xhttp;
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
card = JSON.parse(this.responseText);
document.getElementById("season").innerHTML = card.season;
}
};
xhttp.open("GET", "/curr_card/", true);
xhttp.send();
}
window.onload = updateCard;
setInterval(function() {
if(window.xhttp) {
window.xhttp.abort();
}
updateCard();
}, 2000);
Related
I have my AJAX split in two parts. The "AJHAX" function which has two values, the url of the file to load and then the function where I can specify where I want it to end up ("cFuntion" uses the function "loadToContent"):
First part:
function AJHAX(url, cFunction) {
var xhttp;
// compatible with IE7+, Firefox, Chrome, Opera, Safari
xhttp=new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
cFunction(this);
}
};
xhttp.open("GET", url, true);
xhttp.send();
}
Second part:
function loadToContent(xhttp) {
document.getElementById('content').innerHTML =
xhttp.responseText;
}
So from the first part, I can call the functionality in my menu by adding the following to a link / an element:
onclick="AJHAX('page.php', loadToContent)"
However, with this I can only specify which page to load, not which element I want it loaded to. I tried to add it as a criteria for the second part of my code, like this. But to no avail:
function loadToContent(xhttp, targetElement) {
document.getElementById(targetElement).innerHTML =
xhttp.responseText;
}
Onclick:
onclick="AJHAX('menu.php', loadToContent(sidebar))"
Any suggestions?
Solution has been found (thanks to #schogges, please upvote!). Complete working example:
JS:
function AJHAX(url, targetElement, cFunction) {
var xhttp;
// compatible with IE7+, Firefox, Chrome, Opera, Safari
xhttp=new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
cFunction(this, targetElement);
}
};
xhttp.open("GET", url, true);
xhttp.send();
}
function loadToContent(xhttp, targetElement) {
document.getElementById(targetElement).innerHTML =
xhttp.responseText;
}
HTML:
<div onclick="AJHAX('home.php', 'content', loadToContent);closeSidebar()">
Home</div>
So in the HTML, you just specify the page to load, the ID of the element to load the page into and then the "loadToContent" function that actually loads it into to element. As you can see, also have ";closeSidebar()" in there, and even if it does nothing for this example, I'll just leave it here to show anyone new that you can do it this way to add more than one function to an onclick-event. Mine simply just edits some CSS to hide the sidebar.
What about using a third parameter at AJHAX()?
Try this:
function AJHAX(url, target, cFunction) {
var xhttp;
// compatible with IE7+, Firefox, Chrome, Opera, Safari
xhttp=new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
cFunction(this, target);
}
};
xhttp.open("GET", url, true);
xhttp.send();
}
function loadToContent(xhttp, targetElement) {
document.getElementById(targetElement).innerHTML =
xhttp.responseText;
}
and Onclick:
onclick="AJHAX('menu.php', 'sidebar', loadToContent)"
The line onclick="AJHAX('menu.php', loadToContent(sidebar))" doesn't work because it references an inert string.
Instead what you need to do is to reference a function, so it actually carries out the intended task: onclick = function () { AJHAX('https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js', loadToContent);};
Here is a snippet showcasing my answer:
//First part:
function AJHAX(url, cFunction) {
var xhttp;
// compatible with IE7+, Firefox, Chrome, Opera, Safari
xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
cFunction(this);
}
};
xhttp.open("GET", url, true);
xhttp.send();
}
//Second part:
function loadToContent(xhttp) {
console.log(xhttp.responseText);
alert(xhttp.responseText);
}
//TEST
var button = document.body.appendChild(document.createElement("h1"));
button.textContent = "Click me!";
button.onclick = function () {
AJHAX('https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js', loadToContent);
};
I have checked all the similar questions to this but not found anything that helped... so here goes!
I am writing designing a site for my college project. It simply is an image gallery. I have a counter displayed for each image that increments each time the image is clicked. When the page refreshes the new number is displayed. With me so far?
The problem is that after the database update is completed the return does not complete the rest of the code...
echo "<div class='gridImg'><a href=".$imgpath." data-lightbox='countryside' data-title='".$row['ldesc']."' onclick='"."showUser('".$fname."')'>";
The above line is in a php file and the function in question is showUser, which passes a variable $fname...
function showUser(str) {
if (str === "") {
document.getElementById("txtHint").innerHTML = "";
return;
} else {
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
}
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("txtHint").innerHTML =
this.responseText;
}
};
xmlhttp.open("GET","../php/countrysideupdateviews.php?q="+str,true);
xmlhttp.send();
return;
}
}
The above script takes the passed value and hands it over to countrysideupdateviews.php (Sorry if this script is a mess, I am new to AJAX and took it from the W3Schools site.
<?php
$q = $_GET['q'];
$conn=new mysqli('localhost','user','pass','dbname');
$sql="UPDATE countryside SET views = views + 1 WHERE fname = '".$q."'";
$result=$conn->query($sql);
mysqli_close($conn);
?>
The above php file updates the database.
Ok...
So, a user clicks on one of the images on-screen, which opens a lightbox gallery, BUT also updates the view count and then returns control - except that everything works - the update takes place - but the lightbox does not start, instead a static larger image of the one that was clicked is shown. The only way to clear it is to refresh the site, which does reflect the updated counter.
I have added returns to the onclick function call which does return control but the counter is not updated. Where am I going wrong? Bear in mind please I am still learning and I hope this makes sense :)
The argument to onclick must be a function. showUser("foo") is not a function. You're also missing event.preventDefault() which prevents the click action from opening the link.
Change your showUser to
function showUser(str) {
return function(event) {
if (str === "") {
document.getElementById("txtHint").innerHTML = "";
} else {
if (window.XMLHttpRequest) {
var xmlhttp = new XMLHttpRequest();
}
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("txtHint").innerHTML = this.responseText;
}
};
xmlhttp.open("GET","../php/countrysideupdateviews.php?q="+str,true);
xmlhttp.send();
}
event.preventDefault();
};
}
Ok, after playing around with #apaatsio's answer I got it working, this is what the function now looks like...
function showUser(str) {
if (str === "") {
document.getElementById("txtHint").innerHTML = "";
return;
} else {
if (window.XMLHttpRequest) {
// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
}
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("txtHint").innerHTML = this.responseText;
}
};
xmlhttp.open("GET","../php/countrysideupdateviews.php?q="+str,true);
xmlhttp.send();
event.preventDefault();
}
}
It looks like preventing the click opening the link worked just fine - thanks :)
Okay, this one is weird and I'm not entirely sure how to word the Title for this particular question.
I have a javascript function that is supposed to happen when the document is ready.
The first part of the function calls a function that includes some added html pages into the page.
The next part matches the last section of the current url page and finds them in the menu to to give it a selected class, along with the parent of the menu item.
The code works, but only with the
alert(lastpath);
When the alert statement is removed, the lines below no longer function.
$( document ).ready(function() {
w3IncludeHTML();
lastpath=(window.location.pathname).split("/").slice(-1).pop();
alert(lastpath);
$('a[href$="'+lastpath+'"]').attr("class","selected");
$('a[href$="'+lastpath+'"]').parent(".dropdown-content").prev().attr("class","selected");
});
Does anyone know what could be happening here?
The function w3IncludeHTML defined in the w3Data library loads the content asynchronously. It offers no way to get notified when it has finished its job:
function w3IncludeHTML() {
var z, i, a, file, xhttp;
z = document.getElementsByTagName("*");
for (i = 0; i < z.length; i++) {
if (z[i].getAttribute("w3-include-html")) {
a = z[i].cloneNode(false);
file = z[i].getAttribute("w3-include-html");
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4 && xhttp.status == 200) {
a.removeAttribute("w3-include-html");
a.innerHTML = xhttp.responseText;
z[i].parentNode.replaceChild(a, z[i]);
w3IncludeHTML();
}
}
xhttp.open("GET", file, true);
xhttp.send();
return;
}
}
}
A quick solution would be to alter the above function, and add the following code to your script:
function w3IncludeHTML(callback) {
var z, i, file, xhttp;
z = document.querySelector("[w3-include-html]");
if (!z) {
// notify caller that all is loaded
if (callback) callback();
return;
}
file = z.getAttribute("w3-include-html");
z.removeAttribute("w3-include-html");
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4) {
if (xhttp.status == 200) {
z.innerHTML = xhttp.responseText;
}
w3IncludeHTML(callback);
}
}
xhttp.open("GET", file, true);
xhttp.send();
}
This version will override the function provided by the w3Data library, and improve it. You can now pass a callback function to w3IncludeHTML, in which you can be sure that all content is loaded (unless errors occurred of course):
$( document ).ready(function() {
w3IncludeHTML(function () {
// Everything that depends on loaded content, should be done here:
lastpath=(window.location.pathname).split("/").slice(-1).pop();
// not needed: alert(lastpath);
$('a[href$="'+lastpath+'"]').attr("class","selected");
$('a[href$="'+lastpath+'"]').parent(".dropdown-content").prev().attr("class","selected");
});
});
You use the function from the JS library from w3schools. Just take a look at their code:
function w3IncludeHTML() {
var z, i, a, file, xhttp;
z = document.getElementsByTagName("*");
for (i = 0; i < z.length; i++) {
if (z[i].getAttribute("w3-include-html")) {
a = z[i].cloneNode(false);
file = z[i].getAttribute("w3-include-html");
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4 && xhttp.status == 200) {
a.removeAttribute("w3-include-html");
a.innerHTML = xhttp.responseText;
z[i].parentNode.replaceChild(a, z[i]);
w3IncludeHTML();
}
}
xhttp.open("GET", file, true);
xhttp.send();
return;
}
}
}
It is using a XMLHttpRequest object, so we are sure it's an asynchronous code. Most likely, after your call to this function, you use lines of code which depend on the success of ajax request. This is, of course, not good (treating asynchronous code as a synchronous code), but the delay provided by alert function makes it work (sometimes ;) !).
Solution: make sure, what does the w3IncludeHTML function do and how to get rid of the synchronous code after its call. Or: try to find a way to detect when the ajax part of this function is completed. Actually, it's right there:
if (xhttp.readyState == 4 && xhttp.status == 200) {
a.removeAttribute("w3-include-html");
a.innerHTML = xhttp.responseText;
z[i].parentNode.replaceChild(a, z[i]);
w3IncludeHTML();
}
I have this small script that sends single value to php file, its working in Firefox but not on Chrome and IE
jQuery
var i=1;
$.post("../asset/view/check.php",{i:i},function(data){
alert('hi');
});
Javascript
var i=1;
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4 && xhttp.status == 200) {
alert('hi');
}
};
xhttp.open("GET", "../asset/view/check.php?i="+i+"", false);
xhttp.send();
Can you see why this is not working?
This script is throwing this error every half second:
Uncaught InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable. signals.js:9
req.onreadystatechange signals.js:9
update_table signals.js:26
req.onreadystatechange
This is signals.js - I want it to reload every 5 seconds and if there is new content, to trigger the "Sound" alert.
function update_table()
{
var old_table = document.getElementById('signals').innerHTML;
var req = new XMLHttpRequest();
req.onreadystatechange = function(){
if(req.status == 200)
{
if(req.readyState == 4)
{
var new_table = req.responseText;
alert(old_table);
alert(new_table);
if(old_table != new_table)
{
//play sound
alert("Sound!");
}
alert("Refresh!");
setTimeout(update_table, 5000);
}
}
}
var link = "table.php?refresh=true";
req.open("GET", link, false);
req.send();
}
First check if the req.readyState equals 4 and then check if the req.status equals 200.
The HTTP status code isn't set before the request is processed, so you can't use it before the readyState equals 4.
You can check this link for more info about the onreadystatechange event.
You need to first check whether req.readyState equals to 4 (means DONE), and only then check for req.status:
function update_table() {
var old_table = document.getElementById('signals').innerHTML;
var req = new XMLHttpRequest();
req.onreadystatechange = function() {
// Here: first check for readyState
if(req.readyState === 4 && req.status == 200) {
var new_table = req.responseText;
alert(old_table);
alert(new_table);
if(old_table != new_table)
{
//play sound
alert("Sound!");
}
alert("Refresh!");
setTimeout(update_table, 5000);
}
}
var link = "table.php?refresh=true";
req.open("GET", link, false);
req.send();
}
See XMLHttpRequest doc for details.