how to use jQuerys each with checking an image exists - javascript

I have some source that actually drives me crazy...
First, I have a object like this:
var extensions = {"jpg":".jpg","JPG":".JPG","png":".png","PNG":".PNG"};
Now I want to iterate the object to check if a image with one of these extensions exists. The source for this looks like:
var imgURL = "some/path/";
var imgName = "myFileName";
var counter = 0;
$.each(extensions, function( key, imgExtension ){
var tmpImgUrl = imgURL + imgName + imgExtension;
console.log('test');
$( ".imageCheck" ).unbind().attr( "src", tmpImgUrl ).error( function(){
console.log( tmpImgUrl );
counter++;
if( counter >= Object.keys( extensions ).length){
return false;
}
});
});
This each should take the image name and try to test if there is an error for each extension. Actually it does what it should do. Only thing is, counter doesn't gets counted up and the console log appears wrong.
I expact the log should look like:
test
some/path/myFileName.jpg
test
some/path/myFileName.JPG
test...
and so on. But it appers like:
test
test
test
test
some/path/myFileName.jpg
some/path/myFileName.JPG
...
actually I want to use the image check to return a break (false) for the each so not all extensions get checked. (performance)
And also I really need the counter when exiting the check part....
Any guesses what going wrong here ?

Create a loader to do the image test.
Init the index to test.
Create an <img>, add onload and onerror behavior. onload will notify the user, or you can event let the loader accept callbacks for onload to call it, onerror will try to load image with next type.
The tryLoad function first check if index is equal to extensions.length, if it is, which means all possibles are failed, notify user the image load failed, and do some fail fallback or something.
Otherwise, it use current index with give fileName and url to create imagepath, set it to img.src and increase the index for next attempts.
call the tryload to start the process.
Now the image stops attempt when current url is valid, and will try the extesions one-by-one until no other can be used.
// Inits, can also be put into the loader.
var extensions = ['.jpg', '.JPG', '.png', '.PNG'];
var imgUrl = "some/path/";
var loader = function(fileName, success, fail) {
// Start at first image type.
var index = 0;
// Create a img for loading.
var $image = $('<img>');
// Add success and fail behavior to the image.
$image
.load(function() {
// Do some notification or....
console.log('success', $image.attr('src'));
// If you give a success callback, call it
// Or you can write the logic here.
if ($.isFunction(success)) {
success();
}
})
.error(function() {
console.log('fail', $image.attr('src'));
// Try to load next image
tryLoad(index);
});
// The function for attempts.
var tryLoad = function() {
// When all attemps tried.
if (index === extensions.length) {
alert('Boom, all failed');
// Do some fallbacks....
// $image.remove();
// If you give a fail callback, call it
// Or you can write the logic here.
if ($.isFunction(fail)) {
fail();
}
return;
}
// Create url to load.
var tmpImgUrl = imgURL + fileName + extensions[index];
console.log('test', tmpImgUrl);
$image.attr('src', tmpImgUrl);
++index;
};
// Start the first attempt.
tryLoad();
// return jquery wrapper of image. It'll have image only if any of the test is passed.
return $image;
};

You won't want to use jQuery's .each to loop through an object - the proper way to do this is using javascript's for in loop.
Looping through your extensions object should look like this:
for(var key in extensions){
console.log("Key", key, "Value",extensions.key);
}
You can then do whatever it is you need with those keys or values using the above looping construct.

Related

Javascript - how to synchronously preload images and then callback once they are all loaded

I'm trying to preload images from an array synchronously with a callback when all are done. I can find lots of solutions on here which get me near to where I want to be but none of them work synchronously and allow the usage I require.
The code I have below works great but all are loaded async. I have another script which does the job sequentially but I can't seem to fire a callback once all the items in the array are done. This code below allows for user defined callback outside of the function which is what I need as I'd be calling this function on different arrays.
What I'm attempting to do is preload one array of images, check a timestamp comparison previously stored in a variable to see whether the network is slow or not, then (if the network is slow) preload a second array of images using the same preload function when the first array is done, then fire my image slideshow function once the second array is done. This is to avoid starting my image slideshow before enough images are initially loaded - to avoid blank slides, if that makes sense.
Code which works but only asynchronously:
function preload(arr){
var newimages=[], loadedImages = 0;
var postaction=function(){}
var arr=(typeof arr!="object")? [arr] : arr;
function imageloadpost(){
loadedImages++;
if (loadedImages==arr.length){
postaction(newimages); //call postaction and pass in newimages array as parameter
}
}
for (var i=0; i<arr.length; i++){
newimages[i]=new Image();
newimages[i].src=arr[i];
newimages[i].onload=function(){
imageloadpost();
}
newimages[i].onerror=function(){
imageloadpost();
}
}
return { //return blank object with done() method
done:function(f){
postaction=f || postaction; //remember user defined callback functions to be called when images load
}
}
}
Sample result I want would be:
Image 1...Loaded, then...
Image 2...Loaded, then...
Image 3...Loaded, then...
Callback: all are loaded - do something else
UPDATE:
This is the synchronous code that waits for the first image to load before firing the function again for the second and third etc etc. This is the behaviour I want but can't seem to get a callback to work when all items in the array are loaded.
function preload(arrayOfImages, index) {
index = index || 0;
if (arrayOfImages && arrayOfImages.length && arrayOfImages.length > index) {
var img = new Image();
img.onload = function() {
preload(arrayOfImages, index + 1);
};
img.onerror = function() {
preload(arrayOfImages, index + 1);
};
img.src = arrayOfImages[index];
}
$j('#slide-' + index + '').addClass('loaded');
}

(Image in loop) Huge array of images and wants to display if image is really present then show the images otherwise delete/hide the image node

I have a huge array of images
Actually I am bit worry about checking the image that really exist and also want do something if image present
and if not found I am doing some other work.
Note: If one image is checking on server and takes 3 minutes
then how to handle in loop for other images.
I am just found below image if image not exist :-
Please give a standard way that can be used for nth number of images in array.
var urlArray = [{productImage:'http://www.queness.com/resources/images/png/appleeeeeee_ex.png'},{productImage:'https://www.google.co.in/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png'},{productImage:'https://www.google.co.in/images/branding/googlelogo/1x/googlelogo_cooooooolor_272x92dp.png'},{productImage:'https://www.google.co.in/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png'}];
<div>
<img src=""> // actually this comes through urlArray
</div>
Thanks in advance!
If you want to check image URL exist or not, You can try this with javaScript,
It will check all URLs one by one and call the callBack function, then it will call URL check for next URL.
Next URL will be check after one completes, no matter how much time it will take.
function isImageExists(imgSrc, callBackAfterCheck) {
var img = new Image();
img.onload = function() { callBackAfterCheck(true) };
img.onerror = function() {callBackAfterCheck(false) };
img.src = imgSrc;
}
var urlArray = [{productImage:'http://www.queness.com/resources/images/png/appleeeeeee_ex.png'},{productImage:'https://www.google.co.in/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png'},{productImage:'https://www.google.co.in/images/branding/googlelogo/1x/googlelogo_cooooooolor_272x92dp.png'},{productImage:'https://www.google.co.in/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png'}];
var imageIndex = 0;
function callBackAfterCheck(status){
if(status == true){
//do your stuff here
console.log('image Exist');
}else{
//do your stuff here
console.log('image not Exist');
}
imageIndex++
if(imageIndex < urlArray.length){
var imageUrl = urlArray[imageIndex].productImage;
isImageExists(imageUrl, callBackAfterCheck)
}
}
var imageUrl = urlArray[imageIndex].productImage;
isImageExists(imageUrl, callBackAfterCheck);
Here you can pass your image source and the function you want to execute after checking the source.
It will check your image existence and you can get the status (true or false) in your callBack function,
Now you can do your stuff according to your need in callback function.

Twitch TV JSON API Issue

So,I am trying to use the twitch API:
https://codepen.io/sterg/pen/yJmzrN
If you check my codepen page you'll see that each time I refresh the page the status order changes and I can't figure out why is this happening.
Here is my javascript:
$(document).ready(function(){
var ur="";
var tw=["freecodecamp","nightblue3","imaqtpie","bunnyfufuu","mushisgosu","tsm_dyrus","esl_sc2"];
var j=0;
for(var i=0;i<tw.length;i++){
ur="https://api.twitch.tv/kraken/streams/"+tw[i];
$.getJSON(ur,function(json) {
$(".tst").append(JSON.stringify(json));
$(".name").append("<li> "+tw[j]+"<p>"+""+"</p></li>");
if(json.stream==null){
$(".stat").append("<li>"+"Offline"+"</li>");
}
else{
$(".stat").append("<li>"+json.stream.game+"</li>");
}
j++;
})
}
});
$.getJSON() works asynchronously. The JSON won't be returned until the results come back. The API can return in different orders than the requests were made, so you have to handle this.
One way to do this is use the promise API, along with $.when() to bundle up all requests as one big promise, which will succeed or fail as one whole block. This also ensures that the response data is returned to your code in the expected order.
Try this:
var channelIds = ['freecodecamp', 'nightblue3', 'imaqtpie', 'bunnyfufuu', 'mushisgosu', 'tsm_dyrus', 'esl_sc2'];
$(function () {
$.when.apply(
$,
$.map(channelIds, function (channelId) {
return $.getJSON(
'https://api.twitch.tv/kraken/streams/' + encodeURIComponent(channelId)
).then(function (res) {
return {
channelId: channelId,
stream: res.stream
}
});
})
).then(function () {
console.log(arguments);
var $playersBody = $('table.players tbody');
$.each(arguments, function (index, data) {
$playersBody.append(
$('<tr>').append([
$('<td>'),
$('<td>').append(
$('<a>')
.text(data.channelId)
.attr('href', 'https://www.twitch.tv/' + encodeURIComponent(data.channelId))
),
$('<td>').text(data.stream ? data.stream.game : 'Offline')
])
)
})
})
});
https://codepen.io/anon/pen/KrOxwo
Here, I'm using $.when.apply() to use $.when with an array, rather than list of parameters. Next, I'm using $.map() to convert the array of channel IDs into an array of promises for each ID. After that, I have a simple helper function with handles the normal response (res), pulls out the relevant stream data, while attaching the channelId for use later on. (Without this, we would have to go back to the original array to get the ID. You can do this, but in my opinion, that isn't the best practice. I'd much prefer to keep the data with the response so that later refactoring is less likely to break something. This is a matter of preference.)
Next, I have a .then() handler which takes all of the data and loops through them. This data is returned as arguments to the function, so I simply use $.each() to iterate over each argument rather than having to name them out.
I made some changes in how I'm handling the HTML as well. You'll note that I'm using $.text() and $.attr() to set the dynamic values. This ensures that your HTML is valid (as you're not really using HTML for the dynamic bit at all). Otherwise, someone might have the username of <script src="somethingEvil.js"></script> and it'd run on your page. This avoids that problem entirely.
It looks like you're appending the "Display Name" in the same order every time you refresh, by using the j counter variable.
However, you're appending the "Status" as each request returns. Since these HTTP requests are asynchronous, the order in which they are appended to the document will vary each time you reload the page.
If you want the statuses to remain in the same order (matching the order of the Display Names), you'll need to store the response data from each API call as they return, and order it yourself before appending it to the body.
At first, I changed the last else condition (the one that prints out the streamed game) as $(".stat").append("<li>"+jtw[j]+": "+json.stream.game+"</li>"); - it was identical in meaning to what you tried to achieve, yet produced the same error.
There's a discrepancy in the list you've created and the data you receive. They are not directly associated.
It is a preferred way to use $(".stat").append("<li>"+json.stream._links.self+": "+json.stream.game+"</li>");, you may even get the name of the user with regex or substr in the worst case.
As long as you don't run separate loops for uploading the columns "DisplayName" and "Status", you might even be able to separate them, in case you do not desire to write them into the same line, as my example does.
Whatever way you're choosing, in the end, the problem is that the "Status" column's order of uploading is not identical to the one you're doing in "Status Name".
This code will not preserve the order, but will preserve which array entry is being processed
$(document).ready(function() {
var ur = "";
var tw = ["freecodecamp", "nightblue3", "imaqtpie", "bunnyfufuu", "mushisgosu", "tsm_dyrus", "esl_sc2"];
for (var i = 0; i < tw.length; i++) {
ur = "https://api.twitch.tv/kraken/streams/" + tw[i];
(function(j) {
$.getJSON(ur, function(json) {
$(".tst").append(JSON.stringify(json));
$(".name").append("<li> " + tw[j] + "<p>" + "" + "</p></li>");
if (json.stream == null) {
$(".stat").append("<li>" + "Offline" + "</li>");
} else {
$(".stat").append("<li>" + json.stream.game + "</li>");
}
})
}(i));
}
});
This code will preserve the order fully - the layout needs tweaking though
$(document).ready(function() {
var ur = "";
var tw = ["freecodecamp", "nightblue3", "imaqtpie", "bunnyfufuu", "mushisgosu", "tsm_dyrus", "esl_sc2"];
for (var i = 0; i < tw.length; i++) {
ur = "https://api.twitch.tv/kraken/streams/" + tw[i];
(function(j) {
var name = $(".name").append("<li> " + tw[j] + "<p>" + "" + "</p></li>");
var stat = $(".stat").append("<li></li>")[0].lastElementChild;
console.log(stat);
$.getJSON(ur, function(json) {
$(".tst").append(JSON.stringify(json));
if (json.stream == null) {
$(stat).text("Offline");
} else {
$(stat).text(json.stream.game);
}
}).then(function(e) {
console.log(e);
}, function(e) {
console.error(e);
});
}(i));
}
});

Duplication of images following JavaScript success function running correctly

Each time the "success" function of this code block executes, some unwanted events happen:
If I dont refresh the browser and enter the same name again, a duplicated image appears. This happens each time the code is run.
If I don't refresh the browser and type a name that doesn't exist, wheres before the search returned a name that did, then both images are displayed on the page.
How do I stop the duplication? I've looked at alternatives to .append in jQuery, but none are having the desired result.
I think I also need the query to reset each time its run, other wise it appears this also causes complication.
var friendName;
function findFriend() {
friendName = $('#friendsearch').val();
console.log(friendName);
var query = new Parse.Query(Parse.User);
query.equalTo("username", friendName); // find users that match
query.find({
success: function (friendMatches) {
// This section is always run, no matter the outcome. Depending on the input depends on which result is shown
if (friendMatches.length === 0)
// console.log('NO MATCH FOUND!!!');
$(".no_user").show();
else // Query executed with success
imageURLs = [];
for (var i = 0; i < friendMatches.length; i++) {
var object = friendMatches[i];
imageURLs.push(object.get('pic'));
}
// If the imageURLs array has items in it, set the src of an IMG element to the first URL in the array
for (var j = 0; j < imageURLs.length; j++) {
$('#imgs').append("<img src='" + imageURLs[j] + "'/>");
}
console.log('MATCH FOUND!!!');
},
// Only if the code breaks and cannot either find or not find a user should this error be returned
error: function (error) {
alert('Opps we have a problem' + error.message);
}
});
}
// This captures the users input and is triggered when the user presses the find
$('#find_button').click(function (e) {
findFriend();
});
You need to remove the old image(s) before adding the new one(s):
$('#imgs').empty();
When to clear is another issue (I think this is your 'unwanted event #2'):
success: function (friendMatches) {
// clear images first, so that previous search results
// don't show when the current search returns 0 results
$('#imgs').empty();
...
}

FirefoxOS: return array using device storage api

I am just getting started with coding for FirefoxOS and am trying to get a list of files in a directory.
The idea is to find the name of each file and add it to the array (which works), but I want to return the populated array and this is where I come unstuck. It seems that the array gets populated during the function (as I can get it to spit out file names from it) but when I want to return it to another function it appears to be empty?
Here is the function in question:
function getImageFromDevice (){
var imageHolder = new Array();
var pics = navigator.getDeviceStorage('pictures');
// Let's browse all the images available
var cursor = pics.enumerate();
var imageList = new Array();
var count = 0;
cursor.onsuccess = function () {
var file = this.result;
console.log("File found: " + file.name);
count = count +1;
// Once we found a file we check if there are other results
if (!this.done) {
imageHolder[count] = file.name;
// Then we move to the next result, which call the cursor
// success with the next file as result.
this.continue();
}
console.log("file in array: "+ imageHolder[count]);
// this shows the filename
}
cursor.onerror = function () {
console.warn("No file found: " + this.error);
}
return imageHolder;
}
Thanks for your help!
Enumerating over pictures is an asynchronous call. Essentially what is happening in your code is this:
You are initiating an empty array
You are are telling firefox os to look for pictures on the device
Then in cursor.onsuccess you are telling firefox os to append to the array you have created WHEN it gets back the file. The important thing here is that this does not happen right away, it happens at some point in the future.
Then you are returning the empty array you have created. It's empty because the onsuccess function hasn't actually happened.
After some point in time the onsuccess function will be called. One way to wait until the array is full populated would be to add in a check after:
if (!this.done) {
imageHolder[count] = file.name;
this.continue();
}
else {
//do something with the fully populated array
}
But then of course your code has to go inside the getImageFromDevice function. You can also pass a callback function into the getImageFromDevice function.
See Getting a better understanding of callback functions in JavaScript
The problem is with the aSynchronous nature of the calls you are using.
You are returning (and probably using) the value of imageHolder when it's still empty - as calls to the "onsuccess" function are deferred calls, they happen later in time, whereas your function returns immediately, with the (yet empty) imageHolder value.
You should be doing in this case something along those lines:
function getImageFromDevice (callback){
...
cursor.onsuccess = function () {
...
if (!this.done) {
// next picture
imageHolder[count] = file.name;
this.continue();
} else {
// no more pictures, return with the results
console.log("operation finished:");
callback(imageHolder);
}
}
}
Or use Promises in your code to accomplish the same.
Use the above by e.g.:
getImageFromDevice(function(result) {
console.log(result.length+" pictures found!");
});

Categories