Sometimes, jQuery only updates elements on first page load - javascript

I'm learning javascript by creating a program which requests an API and dispays various properties (price in this example) to html. I have a few questions about my code and some problems I've been facing.
1). I have a bunch of $.getJSON functions corresponding to each value that I want to retrieve. I put them all in a a single 2 min. timer. When the page FIRST loads, however, some of the html elements fail to load at all. But if I refresh the page, they sometimes do load. If I refresh again, they might not load at all again. Every time I refresh, there's like a 10% chance of that particular function not inserting the content in the element. If it does load and I leave the page open, it will correctly function (update its value and html element every 2 mins and add/remove the green and red classes). If it doesn't load and I leave the page open, it will correctly function in 2 mins when the 2nd api request is made. I have already tested that the variables have some value (are not null) before and after each $('#price').text('$' + price);.
Here's an example of a function that does that:
var tempPrice;
var myVar = setInterval(myTimer, 1200000);
myTimer();
function myTimer() {
$.getJSON(link, function (json) {
$.each(json, function (index, value) {
if (value.id == "price") {
var price = value.price_eur;
if (!tempPrice) {
$('#price').text('$' + price);
tempPrice = parseFloat(price);
}
if (parseFloat(price) !== tempPrice) {
$('#price').text('$' + price).removeClass();
if (parseFloat(price) > tempPrice) {
setTimeout(function () {
$('#price').addClass("green");
}, 1);
} else {
setTimeout(function () {
$('#price').addClass("red");
}, 1);
}
tempPrice = parseFloat(price);
}
}
});
});
// Many more $.getJSON functions below...
}
If I run this function alone on either jsfiddle or my dev server (flask), it works fine. It only breaks down when I use it in conjunction with more api requests. If I remember correctly, I didn't have this problem before when I used to have a separate timer for each $.getJSON function and put each in its own <script> tag directly in html.
2) I know I can loop through the json instead of using $.each. How else can I improve the code?

1
As for the problem you're having with the inconsistent behavior of the initial page loading, it's because you are executing JavaScript before giving the browser the time to load the page fully first. You can solve this simply by waiting for the page the load, and then executing your code.
Example in jQuery:
$(document).ready(function() {
// Page is loaded, execute code...
});
2
To help you improve the way you're handling the supplied JSON data, a sample of the data would be useful.

Related

What is the simplest way to alert when the DOM is finished painting new elements?

I just want to disable certain buttons and show a loading spinner until images are loaded.
This is a similar question- How to detect when an image has finished rendering in the browser (i.e. painted)?
I am new to javascript and am confused why there is not a simple way to determine this! Something similar to this-
document.addEventListener('readystatechange', event => {
if (event.target.readyState === 'complete') {
alert('complete');
}
});
Unfortunately after much searching it seems I will need to use a callback or a promise. I figured it would be a good time to learn about promises/async/await. I am having difficulty attempting to rewrite my functions using promises.
$('#container').on('click', '#imgSearch', function (e) {
$(this).html(`
<i class="fa fa-spinner fa-spin"></i>Loading`);
//various if statements here to check radio buttons
showImgs(search, sort, phpController, limit);
}
function showImgs(search, sort, phpController, limit) {
imgArray = [];
var a1 = $.post(phpController, {search: search, sort: sort, limit: limit}, {dataType: 'json'});
$.when(a1).done(function (response) {
if (response['error']) {
alert(response.error.img);
} else {
var imgs = response;
if (Array.isArray(imgs)) {
for (let i = 0; i < imgs.length; i++) {
//I use setTimeout here to render one image per second
setTimeout(function () {
offset++;
saveImages(imgs[i]);
//calling alertFinished on last img is my temporary solution to removing spinner after render
if (i === imgs.length - 1) {
alertFinished();
}
}, 1000 * i);
}
} else {
saveImages(imgs);
}
}
});
}
;
I use the saveImages function to empty and push to another array as well as other purposes not shown-
function saveImages(img) {
imgArray = [];
imgArray.push(img);
displayImages(imgArray);
}
;
displayImages renders the image while adding classes etc.-
function displayImages(imgs) {
var counter = 0;
imgs.forEach(img => {
var html = '';
html += `<div class="" id='${img.id}'><img src=${img.image}></div>`;
$(html).hide().appendTo(imgSection).fadeIn(1000);
});
}
;
AlertFinished removes the loading spinner from search button.
function alertFinished() {
$('#imgSearch').text('Search');
}
Will someone please explain how I can use promises/async/await in this context? It's unfortunate there isn't a way to use an event listener similar to readystatechange because the alternative is to refactor every function that renders new elements in the DOM in order to disable buttons, show spinner etc. Showing a spinner/loading msg is such a widespread feature I am surprised I am having difficulty implementing it appropriately.
So first off, as you can see from the question you linked to (and comments therein), there isn't a great way to tell when an image is actually painted. So I'll focus on a way you can call some function after images are loaded.
For starters, if you have a bunch of images in your page right as it first loads, and you just want to wait for them to load before doing something, you could try the super simple route which would be the window load event.
window.addEventListener('load', (event) => {
// do stuff
});
But I get the impression you have a situation where you're adding images dynamically and want to do something after they're loaded, so that won't work. Still, you're overcomplicating things, I think. A quick look at your function shows you're calling saveImages and displayImages inside a loop even though they appear to be things you want to do only once after you're done adding images.
Assuming that at some point in your whole process you find yourself with a bunch of images that have been added to your DOM, at least some of which are still in the middle of loading, what you need to do is check for the last image to be loaded and then remove your loading spinner afterwards.
The trick here is figuring out which image is last to load. It won't necessarily be the last one you added because images added earlier on could be larger.
One approach you can use to skip the whole promise/async confusion all together would be to use a recursive function that every so often checks whether all images are loaded and if not waits a bit and then checks again. If you make the wait before the function calls itself again relatively short, this will likely work just fine for you.
Something along these lines:
const registeredImages = [];
// add your images
for (let i = 0, l = someImages.length; i < l; i += 1) {
doSomething(); // add someImages[i] to DOM where you want it
someImages[i].setAttribute('data-id', `image-${i}`); // or whatever way to keep track of it
someImages[i].addEventListener('load', register);
}
// remove spinner once they're loaded
tryToRemoveSpinner();
function register(event) {
images.push(event.target.getAttribute('data-id');
}
function tryToRemoveSpinner {
if (registeredImages.length === someImages.length) {
removeSpinner(); // or whatever
} else {
setTimeout(() => { tryToRemoveSpinner() }, 100);
}
}
An enhancement you could add here would be to put some kind of counter on tryToRemoveSpinner so if some image fails to load for whatever reason it eventually just bails out or runs removeSpinner() anyway or whatever you want to do in the event of an error, just in case.
Related reading: How to create a JavaScript callback for knowing when an image is loaded

Page loading status

Is there any way to know how far browser loaded the page?
Either by using JavaScript or browser native functions.
Based on the page status i want to build progress bar.
I'm not 100% sure this will work, but.. here is the theory:
First of all, don't stop JavaScript running until the page has loaded. Meaning, don't use window.ready document.ready etc..
At the top of the page initialise a JavaScript variable called loaded or something and set it to 0.
var loaded = 0;
Throughout the page increment loaded at different points that you consider to be at the correct percentages.
For example, after you think half the page would have been loaded in the code set loaded = 50;.
Etc..
As I say, this is just a concept.
Code:
function request() {
showLoading(); //calls a function which displays the loading message
myRequest = GetXmlHttpObject();
url = 'path/to/script.php';
myRequest.open('GET',url,true);
myRequest.onreadystatechange = function() {
if(myRequest.readyState == 4 && myRequest.status == 200) {
clearLoading(); //calls a function which removes the loading message
//show the response by, e.g. populating a div
//with the response received from the server
}
}
myRequest.send(null);
}
At the beginning of the request I call showLoading() which uses Javascript to dynamically add the equivalent of your preLoaderDiv. Then, when the response is received, I call clearLoading() which dynamically removes the equivalent of your preLoaderDiv.
You'll have to determine it yourself, and to do that you'll have to have a way to determine it. One possibility is to have dummy elements along the page, know their total, and at each point count how many are already present. But that will only give you the amount of DOM obtained, and that can be a very small part of the load time - most often than not, the browser is idle waiting for scripts and images.

Automatic Changes on Multiple Browser

I am in deep trouble with my new requirement.
Suppose there is a form opened in Firefox & IE [Form contains some list of question and a textbox for asking question]. Now if i add some new question [in the Firefox], it needed to be displayed within a few second in the other browsers too [without refreshing the page].
I have tried to call a ajax page using setTimeout. But the problem is i can't use the innerhtml after getting the response from Ajax page. Think about the situation when a user tried to add something on a different browser when the innerHTML inside the ajax call overwrite the page . Please suggest a solution. :(
EDIT:
var auto_refresh = setInterval( function (){
$.post("something.php", { proj_id: $('#proj_id').val() }, function (data) {
if(data) { //alert(data); $('#autoRef').html(data).fadeIn("slow"); } },"html");
},
1000);

How can I set JS to run in the background while remaining unobtrusive?

I have a small chat implementation, which uses a Message model underneath. In the index action, I am showing all the messages in a "chat-area" form. The thing is, I would like to start a background task which will poll the server for new messages every X seconds.
How can I do that and have my JS unobtrusive? I wouldn't like to have inline JS in my index.html.erb, and I wouldn't like to have the polling code getting evaluated on every page I am on.
This would be easiest using a library like mootools or jquery. On domready/document.ready, you should check for a given class, like "chat-area-container". If it is found, you can build a new <script> tag and inject it into DOM in order to include the javascript specific for the chat area. That way, it isn't loaded on every page. The "chat-area-container" can be structured so that it is hidden or shows a loading message, which the script can remove once it is initialized.
On the dynamically created <script> tag, you add an onLoad event. When the script is finished loading, you can call any initialization functions from within the onLoad event.
Using this method, you can progressively enhance your page - users without javascript will either see a non-functioning chat area with a loading message (since it won't work without js anyway), or if you hide it initially, they'll be none-the-wiser that there is a chat area at all. Also, by using a dynamic script tag, the onLoad event "pseudo-threads" the initialization off the main javascript procedural stack.
To set up a poll on a fixed interval use setInterval or setTimeout. Here is an example, using jQuery and making some guesses about what your server's ajax interface might look like:
$(function() {
// Look for the chat area by its element id.
var chat = $('#chat-area');
var lastPoll = 0;
function poll() {
$.getJSON('/messages', { since: lastPoll }, function(data) {
// Imagining that data is a list of message objects...
$.each(data, function(i, message) {
// Create a paragraph element to display each message.
var m = $('<p/>', {
'class': 'chat-message',
text: message.author +': '+ message.text;
});
chat.append(m);
});
});
// Schedules the function to run again in 1000 milliseconds.
setTimeout(poll, 1000);
lastPoll = (new Date()).getTime();
}
// Starts the polling process if the chat area exists.
if (chat.length > 0) {
poll();
}
});

How to make my function to be page specific or div id specific?

I am writing javascript to my web pages, but there is a number of functions and loops, that i think are running in all pages, so the first one is running and failing on the second page. Because of this, the javascript function on the second page is not running.
Can anyone give me an idea of how to create page-specific functions or check the availability of an id? I don't use any frameworks.
thanks in advance.
my javascript code is :
window.onload = function(){
var yellows = document.getElementById('magazine-brief').getElementsByTagName('h2');
var signUp = document.getElementById('signup-link');
function animeYellowBar(num){
setTimeout(function(){
yellows[num].style.left = "0";
if(num == yellows.length-1){
setTimeout(function(){
signUp.style.webkitTransform = "scale(1)";
},num * 250);
}
}, num * 500);
}
for (var i = 0; i < yellows.length; i++){
animeYellowBar(i);
}
alert("alert second page");
}
in this code, the alert message not working on second page. any idea?
If I understand you correctly, you have a javascript function, that you want to attach to an event from a specific div element in your page.
a) Include an event directly to you HTML page, something like this:
<div id="element" onclick="some_function();">Text is here</div>
b) Use a javascript function (add this code between <script> tag):
window.onload = function() {
document.getElementById("element").setAttribute("onclick", "some_function()")
}
The best way would be to only include those scripts on the pages which need them. Why waste time loading and parsing scripts you don't need?
If you must keep them on every page, put your functions in an if statement and check for something unique to the page that needs them (such as a form element or field ID).
Update
In response to your comment:
You have to code more defensively. You are attempting to make use of the magazine-brief and signup-link elements before you have made certain that they exist. Never trust that the proper element was returned - always check that it was before attempting to use that element.
I suggest checking your vars like so:
var yellows = document.getElementById('magazine-brief').getElementsByTagName('h2');
var signUp = document.getElementById('signup-link');
if (yellows != 'undefined' && signUp != undefined)
{
function animeYellowBar(num)
{
//...
}
}

Categories