Javascript access callback function results - javascript

I am not new to programming (Fortran, Python) but definitively new to javascript. I also apologize for below function having been taken from another Stack Overflow post but that is the best example I found for for helping me understand want I am trying.
I am writing a Reveal.js presentation that shall show satellite pictures named "sat_picts" + ymd + hr + min + ".jpg"". The pictures are updated every quarter of an hour but it may happen that when they are called the latest one is not yet available so I want to present the one before.
My readings make me understand that this is a characteristic of asynchronous behaviour of the problem and that callback functions are one way of solving the issue.
Unfortunately none of the examples or readings I found show explicitely how to retrieve the results of the callback function in my case "error" and "success". I need these results for an "if, else if" block which then writes in the HTML section the actual or the previous satellite pictures.
I would appreciate if someone could show how to resolve my issue or ev. how to better implement such a solution and or indicate me an appropriate reading.
<section> // Defines Reveal.js slide
<h5>Satellite Picture</h5>
<script>
function testImage(url, callback) {
let img = new Image();
img.onerror = img.onabort = function() {
// callback(url, "error");
let resx = callback(url, "error"); // thought was way to access resx i.e. "error"
alert(resx); // alert is working properly
}
img.onload = function() {
callback(url, "success");
let resy = callback(url, "success"); // thought was way to access resy i.e. "success"
alert(resy); // alert is working properly
}
img.src = url;
};
function record(url, result) {
// document.write(url, result); // ==> returns result but not in the Reveal.js HTML Section
return result;
};
let imgurl = "https://server/sat_picts" + ymd + hr + min + ".jpg";
testImage(imgurl, record);
// If the picture can be loaded then write satellite picture to document
if (resy === "success") document.write('<img src="https://server/sat_picts" + ymd + hr + min + ".jpg">')
// If the the picture cannot be loaded then write satellite picture from previous hour to document
else if (resx === "error") document.write('<img src="https://server/sat_picts" + ymd + hr + min_minus_15 + ".jpg">');
</script>
</section>

If I understand what you are trying to do, I think this is what you want:
<section id="targetSection">
<script>
// allow a success and error call back to be passed in as separate args
function testImage(url, successCallback, errorCallback) {
let img = new Image();
img.onerror = img.onabort = function() {
errorCallback();
}
img.onload = function() {
successCallback();
}
img.src = url;
};
// If the picture can be loaded then write satellite picture to document
function. onSuccess() {
// write the new image to the "section"
const img = document.createElement('img');
img.setAttribute('src', "https://server/sat_picts" + ymd + hr + min + ".jpg");
document.getElementById('targetSection').appendChild(img);
}
// If the the picture cannot be loaded then write satellite picture from previous hour to document
function onError() {
// write the new image to the "section"
const img = document.createElement('img');
img.setAttribute('src', "https://server/sat_picts" + ymd + hr + min_minus_15 + ".jpg");
document.getElementById('targetSection').appendChild(img);
}
let imgurl = "https://server/sat_picts" + ymd + hr + min + ".jpg";
// pass the two callbacks to be called on success or error respectively.
testImage(imgurl, onSuccess, onError);
</script>
</section>

I could be wrong about this, but it looks like the error might be the way the img tags are written in the document.write functions. The img tag needs to be closed with a '/' at the end:
<img src="https://server/sat_picts" + ymd + hr + min + ".jpg" />
The other possibility I can think of is a little gotcha in HTML, which is: when the containing element only has an image tag and no other content, then they won't show the image unless they have some dimensions (height, optionally width) associated with them, so you can check your HTML and if necessary add a CSS property to it that gives it some height (and/or width) to see if this is the issue.
e.g.
<div><img src="myImg.jpg" /></div>
would show nothing if the containing div hasn't been given a height (it will automatically span the width of the parent element).

Related

Find the dimensions of an image from bulk URLS

Hello friends of StackOverflow!
I have a txt file with hundreds of web URLS that point to images. The images are all in JPG form.
I am trying to find out the dimensions of all the images in bulk without having to go through and inspect element on every single one of them. That would take hours. I thought about creating some sort of loop in javascript that reads the lines one by one but I am stumped. I do have a code that tells me the image dimensions but it doesn't perform the operation in bulk. I have to replace the URL of the image every time.
How would I go about this? It would be ideal if I had a piece of code (no language preference) that can read the txt file line by line and write the image dimensions corresponding to the line read in a new text file.
Here is my code so far: https://codepen.io/th3pr099/pen/XGVoMp
function getMeta(url, callback) {
var img = new Image();
img.src = url;
img.onload = function() { callback(this.width, this.height); }
}
getMeta(
"https://www.w3schools.com/w3css/img_lights.jpg",
function(width, height) { alert("Width: " + width + 'px ' + "Height: "
+ height + 'px') }
);
Thank you so much for your help!
Here's an idea, written in JavaScript (notice that it uses your getMeta function.):
<!doctype html>
<html>
<body>
Below is the textarea element that will take in the contents of your text file:<br />
<textarea id="txtarea"></textarea>
<button id="btn">Get image dimensions</button><br/><br/><br/>
<script>
//Your `getMeta` function goes below (I found that it works great)
function getMeta(url, callback) {
var img = new Image();
img.src = url;
img.onload = function() { callback(this.width, this.height); }
}
//When the button (id of "btn") is pressed,
document.getElementById("btn").onclick=function() {
//Get the value of the textarea, then split it on newlines
var listOfLinks=document.getElementById("txtarea").value.split("\n");
//iterate through each line
for (var i=0;i<listOfLinks.length;i++) {
//run your `getMeta` function to get width and height
getMeta(listOfLinks[i],function(w,h) {
//Append data to body
document.body.innerHTML+="Image Dementions: ("+w+","+h+")<br />";
});
}
};
</script>
</body>
</html>

Make a new image appear by clicking on the previous image

I want to write a code in which when you click on an image another image appears. After that when you click on the new image, another one appears, and so on.
I wrote this code which works for the first image. I can't figure out how to define the appeared images as inputs.
var i = 1
function addimage() {
var img = document.createElement("img");
img.src = "images/d" + i + ".jpg";
document.body.appendChild(img);
}
function counter() {
i = i + 1
}
<input type="image" src="images/d1.jpg" onclick="addimage(); counter();">
Attach an onclick function to the new image, with the same code as in your input tag:
var i = 1
function imageClick() {
if (! this.alreadyClicked)
{
addimage();
counter();
this.alreadyClicked = true;
}
}
function addimage() {
var img = document.createElement("img");
img.src = "http://placehold.it/" + (200 + i);
img.onclick = imageClick;
document.body.appendChild(img);
}
function counter() {
i = i + 1
}
<input type="image" src="http://placehold.it/200" onclick="imageClick();">
To add an event handler to an element, there are three methods; only use one of them:
=> With an HTML attribute. I wouldn't recommend this method because it mixes JS with HTML and isn't practical in the long run.
<img id="firstImage" src="something.png" onclick="myListener(event);" />
=> With the element's attribute in JS. This only works if you have a single event to bind to that element, so I avoid using it.
var firstImage = document.getElementById('firstImage');
firstImage.onclick = myListener;
=> By binding it with JavaScript. This method has been standardized and works in all browsers since IE9, so there's no reason not to use it anymore.
var firstImage = document.getElementById('firstImage');
firstImage.addEventListener("click", myListener);
Off course, myListener needs to be a function, and it will receive the event as its first argument.
In your case, you probably don't want to add another image when you click on any image that isn't currently the last. So when a user clicks on the last image, you want to add a new image and stop listening for clicks on the current one.
var i = 1;
function addNextImage(e) {
// remove the listener from the current image
e.target.removeEventListener("click", addNextImage);
// create a new image and bind the listener to it
var img = document.createElement("img");
img.src = "http://placehold.it/" + (200 + i);
img.addEventListener("click", addNextImage);
document.body.appendChild(img);
// increment the counter variable
i = i + 1;
}
var firstImage = document.getElementById("firstImage");
firstImage.addEventListener("click", addNextImage);
Try on JSFiddle
On a side note: while JavaScript does support omitting some semi-columns it's considered a better practice to put them, and it will avoid small mistakes.

How do I add loading indicator to an <img>?

I have the following HTML in my project.
<div class="container" id="crop">
<img id="timage" src="http://example.com/color/style/etc/" alt="timages" />
I also have the following javascript:
$(window).load(function () {
$("#slider").change(function update() {
sVal = $(this).val();
if (sVal == 2) {
$('#timage').prop('src',"http://example.com/" +
tForm +
"color.blahblah" +
itemCode +
"therest_ofthe_URL");}
sVal = $(this).val();
if (sVal == 3) {
$('#timage').prop('src',"http://example.com/" +
tForm +
"color.blahblah" +
itemCode +
"therest_ofthe_URL");}
);}
It works splendidly to replace the image with the string when the slider value reaches certain numbers. The problem is, the image is being created on the back end behind the scenes and takes quite some time before it is ready. In the meantime, you are just staring at the original image wondering if the slider did anything.
How do I add a loading indicator to let people know that the image is about to change?
First, place a loading indicator where you want it. You could replace #timage with a spinning gif, for example. Then use this code to start the new image loading:
var img = new Image();
img.onload = function () {
$('#timage').prop('src', img.src);
}
img.src = '/image/to/load/here';
The function will be executed when your new image has been retrieved from the server and loaded. Since it's already cached on the client, it should load instantly once the src for #timage is set.

Preload image for seamless background image change in JavaScript

I have a Python websocket application which sends an image path to a JavaScript HTML file. When the image path is received, I change the background image of the webpage to the supplied image.
The issue I'm having at the moment, is when the background changes from the old image to the new, there is a momentary 'flash' of which, which suggests that there is a period of time (albeit very brief) where the new image is being loaded.
I've tried various preloading methodologies, but I'm very new to JavaScript, so am not sure which method would provide for a seamless transition between the two images. This is the method I currently have implemented:
var NewImage = new Image();
NewImage = message.data; //This is the image string received from Python
document.body.style.backgroundImage = "url('" + NewImage + "')";
The above displays the image as desired (including my CSS formatting), but the transition is unsightly.
I also had a play around with the following method, which makes more sense to me, but I couldn't get it to work.
var NewImage = new Image();
//Websocket function here
PreloadImage;
function PreloadImage() {
NewImage.onload = ImageLoadComplete();
NewImage.src = message.data;
}
function ImageLoadComplete() {
document.body.style.backgroundImage = "url('" + NewImage + "')"
}
I'm not sure how to pass variables between the functions in this second method. Given the explicit 'onload' call in this method, I feel that this may provide the functionality I'm after.
How can I preload the images in order to seamlessly transition between them?
EDIT: The working code is posted below. Thanks to #blender for pointing me in the right direction :)
var NewImage = new Image;
NewImage.src = message.data; //Data from Python
if (NewImage.complete) {
NewImage.onload = ImageLoadComplete();
} else {
NewImage.onload = ImageLoadComplete;
}
function ImageLoadComplete() {
document.body.style.backgroundImage = "url('" + NewImage.src + "')";
}
You're not actually passing a callback function:
NewImage.onload = ImageLoadComplete();
You're passing in the result of calling ImageLoadComplete(), which means you call your callback immediately. Don't call the function and your code should work as expected (most of the time):
NewImage.onload = ImageLoadComplete;
One issue that you'll encounter is that onload may not get called by some browsers if the image is loaded from cache. You have to call the callback manually if that's the case:
if (NewImage.complete || NewImage.height > 0) {
ImageLoadComplete();
} else {
NewImage.onload = ImageLoadComplete;
}

Changing JS code from clicking image to clicking link

I found the following JS online, which functions like:
If an image is clicked, open the image in new window and prompt for print. Once printed the window closes. I need this script modified to click a print link it prints an image then closes the new image window. So I want to change from clicking the image itself to clicking a link that says print image.
Here is the code:
<script type="text/javascript">
/* <![CDATA[ */
function makepage(src)
{
// We break the closing script tag in half to prevent
// the HTML parser from seeing it as a part of
// the *main* page.
return "<html>\n" +
"<head>\n" +
"<title>Temporary Printing Window</title>\n" +
"<script>\n" +
"function step1() {\n" +
" setTimeout('step2()', 10);\n" +
"}\n" +
"function step2() {\n" +
" window.print();\n" +
" window.close();\n" +
"}\n" +
"</scr" + "ipt>\n" +
"</head>\n" +
"<body onLoad='step1()'>\n" +
"<img src='" + src + "'/>\n" +
"</body>\n" +
"</html>\n";
}
function printme(evt)
{
if (!evt) {
// Old IE
evt = window.event;
}
var image = evt.target;
if (!image) {
// Old IE
image = window.event.srcElement;
}
src = image.src;
link = "about:blank";
var pw = window.open(link, "_new");
pw.document.open();
pw.document.write(makepage(src));
pw.document.close();
}
/* ]]> */
</script>
<img src="fortune.jpg" onclick="printme(event)" />
I do not know any JS so I apologize. I only do php/mysql.
Best Regards!
Jim.
<img src="someurl.jpg" id="imgid' />
Print the image
function printme(id)
{
var src = document.getElementById(id).src;
var link = "about:blank";
var pw = window.open(link, "_new");
pw.document.open();
pw.document.write(makepage(src));
pw.document.close();
}
In the old method, the printme function knows what image should be printed: the same image that was clicked; when you change the trigger, you need to tell the function explicitly what image you want to print. That is why we are adding an id to the image and pass it to printme function. But if you only have one image on the page, or if a spacial relation exists (like the link always being the immediate next node after the image), then we can do it differently and need no id.
I don't know what your link looks like, but assuming it's just a plain anchor:
Change from:
<img src="fortune.jpg" onclick="printme(event)" />
To:
<img id="printableImage" src="fortune.jpg" onclick="printme(event)" />
<a href="javascript:void(0);" onclick="printme('printableImage')" />
Then alter the printme() function to retrieve the image src from the passed in image element id (jQuery makes this easy):
function printme(printableImageId)
{
var src = $('#' + printableImageId).attr('src');
// the rest of the logic...
}
You'll need to modify the printMe function a bit, doing this should fix it:
function printme(evt)
{
if (!evt) {
// Old IE
evt = window.event;
}
var anchor = evt.target;
if (!anchor) {
// Old IE
anchor = window.event.srcElement;
}
src = anchor.href;
link = "about:blank";
var pw = window.open(link, "_new");
pw.document.open();
pw.document.write(makepage(src));
pw.document.close();
return false;
}
And then on your actual anchor you would do the following:
Print

Categories