refresh multiple images using Javascript - javascript

I have built a page that pulls in multiple images from various webcams that we use the office to monitor road networks.
We want the images to update automatically every 30 seconds, ut my JavaScript isn't brilliant I'm afraid.
I know I can refresh the whole page easily, and I can refresh a single image just fine too. But multiple images from different URLs is proving more difficult.
The camera images are the only images on the page, if that's useful, and are displayed in HTML like such:
<figure class="fluid tiles">
<img src="cam/17002.php" alt="M" onerror="this.src = 'camoffline.png';" />
<figcaption class="textStyle">M20 00/1A J10-11<br /><small>Camera: 17002</small></figcaption>
</figure>

// get all of the images from the page
const images = document.getElementsByTagName( 'img' );
// perform this function every 30,000 ms (30 sec)
const timer = setInterval( function(){
// go through each image and reference a custom attribute
// 'data-source' to preserve the original image's src
for( var i=0, x=images.length; i<x; i++ ){
// select each image, individually
let image = images[i];
let source = image.getAttribute( 'data-source' );
// if 'data-source' does not exist, create it
if( !source ){
source = image.src;
image.setAttribute( 'data-source', source );
// give 'data-source' the original image path
}
// Add a timestamp to the image source to help mitigate
// browser caching
image.src = source + '?t=' + Date.now();
}
}, 30000 );
img{ height: 150px; }
<img src="https://image.shutterstock.com/z/stock-photo--week-old-cocker-spaniel-puppy-630877553.jpg">
<img src="https://image.shutterstock.com/z/stock-photo--week-old-cocker-spaniel-puppy-630877553.jpg">
<img src="https://image.shutterstock.com/z/stock-photo--week-old-cocker-spaniel-puppy-630877553.jpg">
<img src="https://image.shutterstock.com/z/stock-photo--week-old-cocker-spaniel-puppy-630877553.jpg">
<img src="https://image.shutterstock.com/z/stock-photo--week-old-cocker-spaniel-puppy-630877553.jpg">
Use .getElementsByTagName('img') to collect all the images that exist on your page.
Then, in an interval (setInterval: code that will continue to execute every ### miliseconds) create a for-loop that will iterate through the HTML collection captured with .getElementsByTagName()
One idea is to create a new attribute on each image on your page, 'data-source' (data- attributes can really be anything). 'data-source' will keep the original information for the path to the image file.
Then, using the value from the original image path, reassign that image path value to the image, but add in a timestamp as a query parameter. This is to help the browser load a new image (browser's love caching content, where they can). The browser sees image.jpeg?t=1234 and thinks it is a different image than image.jpeg?t=1233

I want to propose a slightly modified solution that uses a <canvas>. This will paint all of your sources in a fixed width container and, if they are offline, draw the "Offline" text where the source should have been.
Click the checkbox to test the offline functionality.
const sources = [
"https://cdn.sstatic.net/Sites/stackoverflow/img/sprites.svg",
"https://cdn.sstatic.net/Sites/stackoverflow/img/sprites.svg"
];
function getSources(offline = false) {
return !offline ?
sources.map(src => `${src}?cb=${Date.now()}`) :
sources.map(() => "");
}
function loadImage(src) {
return new Promise((resolve, reject) => {
const img = new Image();
img.addEventListener("load", () => resolve(img));
img.addEventListener("error", () => reject(`Problem loading ${src}`));
img.src = src;
})
}
const offlineCheckbox = document.getElementsByTagName("input")[0],
canvas = document.getElementsByTagName("canvas")[0],
ctx = canvas.getContext("2d");
const {
width,
height
} = canvas;
function refresh() {
console.log("Updating");
getSources(offlineCheckbox.checked).forEach((source, idx, arr) => {
const w = width / arr.length,
dx = w * idx,
dWidth = w * (idx + 1);
loadImage(source)
.then(img => {
ctx.clearRect(dx, 0, dWidth, height);
ctx.drawImage(img, dx, 0, dWidth, height);
})
.catch(err => {
ctx.clearRect(dx, 0, dWidth, height);
ctx.strokeText("Offline", dx, height * 0.5, dWidth);
})
});
setTimeout(() => refresh(), 3 * 1000);
}
refresh();
<main>
<div>
<label><input type="checkbox"> Pretend to be offline</label>
</div>
<div>
<canvas width="400" height="400"></canvas>
</div>
</main>

You can use a timer to reset the src of the image every 30 seconds:
var img = document.querySelector("img.cam");
setInterval(function(){
// I'm appending # and a date to ensure we don't use a cached version of the file
img.src = "https://picsum.photos/200/300/?random#" + new Date() ;
}, 2000); // number is in milliseconds
img { width: 100px; }
<figure class="fluid tiles">
<img class="cam" src="cam/17002.php" alt="M" onerror="this.src='camoffline.png';" />
<figcaption class="textStyle">M20 00/1A J10-11<br /><small>Camera: 17002</small></figcaption>
</figure>

Related

How to access document.body from a script inside of document.write

Help! I am working on an application which downloads a report from an API which gives me HTML as part of the JSON return. Because it returns JSON I cannot simply open this in a new window.
In this script I am injecting this code:
let tmp = result.data;
let spot = tmp.indexOf('</body>');
let insert = `<div style="position:fixed; top:200px;left:20px; width:200px;font-size:15pt">
<div class="camera">
<video id="video" style='display:hidden'>Video stream not available.</video>
<button onClick='takepicture()'>Take photo</button>
</div>
<canvas id="canvas" style='display:none;'></canvas>
<!--<button onClick='genPDF()'>PDF</button>-->
Press CTRL+S to save page as HTML
<img id="testDownloadImage" src='' width=600 height=400 />
</div>
<script type='text/javascript'>
alert(document.body.innerHtml)
// function clearphoto() {
// var context = canvas.getContext('2d');
// context.fillStyle = "#AAA";
// context.fillRect(0, 0, canvas.width, canvas.height);
// var data = canvas.toDataURL('image/png');
// // photo.setAttribute('src', data);
// }
// Capture a photo by fetching the current contents of the video
// and drawing it into a canvas, then converting that to a PNG
// format data URL. By drawing it on an offscreen canvas and then
// drawing that to the screen, we can change its size and/or apply
// other changes before drawing it.
async function takepicture() {
let video = document.getElementById('video');
let canvas = document.getElementById('canvas');
let displayMediaOptions={video: true,displaySurface:'browser', logicalSurface:true, cursor:'never', audio: false};
try{
video.srcObject = await navigator.mediaDevices.getDisplayMedia(displayMediaOptions);
// video.play();
var context = canvas.getContext('2d');
width=window.innerWidth;
height=window.scrollHeight;
if (width && height) {
canvas.width = width;
canvas.height = height;
context.drawImage(video, 0, 0, width, height);
var data = canvas.toDataURL('image/jpg'); //.replace(/^data:image\/(png|jpg);base64,/, "");
// photo.setAttribute('src', data);
document.getElementById('testDownloadImage').src=data;
// location.href=data;
} else {
// clearphoto();
}
}
catch(err) {
console.log("An error occurred: " + err);
}
finally{
let tracks = video.srcObject.getTracks();
tracks.forEach(track => track.stop());
video.srcObject = null;
}
}
</script>
`
let newStr = [tmp.slice(0, spot), insert, tmp.slice(spot)].join('');
document.write(newStr);
to allow me to add a print screen option so that the report can be imaged. I have tried other utilities to save the HTML as a PDF or even saving the page but because the report uses a lot of XSLT and scripting to display the results these methods fail, leaving me with the print screen as the only option.
WebRTC handles this aptly, with one minor difficult: Because I am using document.write I am unable to find the document.body even though the code I am passed is a properly formed page ( etc).
How can I use the inserted script to display the page and capture it as an image?
Thanks
This is not a genuine answer sadly but I resolved the problem by moving the entire write to an iFrame with the script on the outside. I then got the scrollHeight of the iFrame document and resized the frame to match that, effectively making it appear as if the document occupied the entire page. This way I can place my script outside the iFrame and still use it to capture the entire page, which is what I was shooting for.
Thank you for any comments. As of now this is no longer needed.

How to generate video thumbnails and preview them while hovering on the progress bar?

I want to generate video thumbnail and preview it while hovering on the progress bar like YouTube video:
I've tried to test with videojs-thumbnails but failed. The README file doesn't contain enough information to fix it.
I've also tried to search on Google with keyword: video thumbnail progress bar. There are some related questions on SO but I can't find the solution for this case.
I found a javascript library videojs which contains event hovering on progress bar:
videojs('video').ready(function () {
$(document).on('mousemove', '.vjs-progress-control', function() {
// How can I generate video thumbnails and preview them here?
});
});
Currently (Dec. 2019), there are not so much javascript (both free version and paid version) library which supports adding thumbnail while hovering on video progress bar.
But you can follow on the road of videojs. They've already supported adding tooltip while hovering on video progress bar. Everything else you can do is Generating video thumbnails and adding them into the control bar for previewing.
In this example, we will explain about how to generate video thumbnail from <input type="file" />. Athough we can use video source with a directly link, in testing period, we have some problem with Tainted canvases may not be exported because of using canvas.toDataURL()
After videojs completes initializing, you can clone a new video from the source and append it to the body. Just to play and catch loadeddata event:
videojs('video').ready(function () {
var that = this;
var videoSource = this.player_.children_[0];
var video = $(videoSource).clone().css('display', 'none').appendTo('body')[0];
video.addEventListener('loadeddata', async function() {
// asynchronous code...
});
video.play();
});
Like YouTube video thumbnail, we will generate thumbnail file as an image. This image has a size:
(horizontalItemCount*thumbnailWidth)x(verticalItemCount*thumbnailHeight) = (5*158)x(5*90)
So 790x450 is the size of the image which contains 25 sub-thumbnails (YouTube uses 158 as the width and 90 as the height of the thumbnail). Like this:
Then, we will take video snapshot based on video duration. In this example, we generate thumbnail per second (each second has a thumbnail).
Because generating video thumbnail needs a long time based on video duration and quality, so we can make a default thumbnail with a dark theme for waiting.
.vjs-control-bar .vjs-thumbnail {
position: absolute;
width: 158px;
height: 90px;
top: -100px;
background-color: #000;
display: none;
}
After getting video duration:
var duration = parseInt(that.duration());
we need to parse it to an int before using in the loop because the value may be 14.036.
Everything else is: Setting the currentTime value of the new video and converting the video to canvas.
Because 1 canvas element can contains maximum 25 thumbnails by default, we have to add 25 thumbnails to the canvas one-by-one (from left to right, from top to bottom). Then we store it in an array.
If there is still another thumbnail, we create another canvas and repeat the action
var thumbnails = [];
var thumbnailWidth = 158;
var thumbnailHeight = 90;
var horizontalItemCount = 5;
var verticalItemCount = 5;
var init = function () {
videojs('video').ready(function() {
var that = this;
var videoSource = this.player_.children_[0];
var video = $(videoSource).clone().css('display', 'none').appendTo('body')[0];
// videojs element
var root = $(videoSource).closest('.video-js');
// control bar element
var controlBar = root.find('.vjs-control-bar');
// thumbnail element
controlBar.append('<div class="vjs-thumbnail"></div>');
//
controlBar.on('mousemove', '.vjs-progress-control', function() {
// getting time
var time = $(this).find('.vjs-mouse-display .vjs-time-tooltip').text();
//
var temp = null;
// format: 09
if (/^\d+$/.test(time)) {
// re-format to: 0:0:09
time = '0:0:' + time;
}
// format: 1:09
else if (/^\d+:\d+$/.test(time)) {
// re-format to: 0:1:09
time = '0:' + time;
}
//
temp = time.split(':');
// calculating to get seconds
time = (+temp[0]) * 60 * 60 + (+temp[1]) * 60 + (+temp[2]);
//
for (var item of thumbnails) {
//
var data = item.sec.find(x => x.index === time);
// thumbnail found
if (data) {
// getting mouse position based on "vjs-mouse-display" element
var position = controlBar.find('.vjs-mouse-display').position();
// updating thumbnail css
controlBar.find('.vjs-thumbnail').css({
'background-image': 'url(' + item.data + ')',
'background-position-x': data.backgroundPositionX,
'background-position-y': data.backgroundPositionY,
'left': position.left + 10,
'display': 'block'
});
// exit
return;
}
}
});
// mouse leaving the control bar
controlBar.on('mouseout', '.vjs-progress-control', function() {
// hidding thumbnail
controlBar.find('.vjs-thumbnail').css('display', 'none');
});
video.addEventListener('loadeddata', async function() {
//
video.pause();
//
var count = 1;
//
var id = 1;
//
var x = 0, y = 0;
//
var array = [];
//
var duration = parseInt(that.duration());
//
for (var i = 1; i <= duration; i++) {
array.push(i);
}
//
var canvas;
//
var i, j;
for (i = 0, j = array.length; i < j; i += horizontalItemCount) {
//
for (var startIndex of array.slice(i, i + horizontalItemCount)) {
//
var backgroundPositionX = x * thumbnailWidth;
//
var backgroundPositionY = y * thumbnailHeight;
//
var item = thumbnails.find(x => x.id === id);
if (!item) {
//
//
canvas = document.createElement('canvas');
//
canvas.width = thumbnailWidth * horizontalItemCount;
canvas.height = thumbnailHeight * verticalItemCount;
//
thumbnails.push({
id: id,
canvas: canvas,
sec: [{
index: startIndex,
backgroundPositionX: -backgroundPositionX,
backgroundPositionY: -backgroundPositionY
}]
});
} else {
//
//
canvas = item.canvas;
//
item.sec.push({
index: startIndex,
backgroundPositionX: -backgroundPositionX,
backgroundPositionY: -backgroundPositionY
});
}
//
var context = canvas.getContext('2d');
//
video.currentTime = startIndex;
//
await new Promise(function(resolve) {
var event = function() {
//
context.drawImage(video, backgroundPositionX, backgroundPositionY,
thumbnailWidth, thumbnailHeight);
//
x++;
// removing duplicate events
video.removeEventListener('canplay', event);
//
resolve();
};
//
video.addEventListener('canplay', event);
});
// 1 thumbnail is generated completely
count++;
}
// reset x coordinate
x = 0;
// increase y coordinate
y++;
// checking for overflow
if (count > horizontalItemCount * verticalItemCount) {
//
count = 1;
//
x = 0;
//
y = 0;
//
id++;
}
}
// looping through thumbnail list to update thumbnail
thumbnails.forEach(function(item) {
// converting canvas to blob to get short url
item.canvas.toBlob(blob => item.data = URL.createObjectURL(blob), 'image/jpeg');
// deleting unused property
delete item.canvas;
});
console.log('done...');
});
// playing video to hit "loadeddata" event
video.play();
});
};
$('[type=file]').on('change', function() {
var file = this.files[0];
$('video source').prop('src', URL.createObjectURL(file));
init();
});
.vjs-control-bar .vjs-thumbnail {
position: absolute;
width: 158px;
height: 90px;
top: -100px;
background-color: #000;
display: none;
}
<link rel="stylesheet" href="https://vjs.zencdn.net/7.5.5/video-js.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="https://vjs.zencdn.net/7.5.5/video.js"></script>
<input type="file" accept=".mp4" />
<video id="video" class="video-js vjs-default-skin" width="500" height="250" controls>
<source src="" type='video/mp4'>
</video>
Fiddle
I struggled with this exact issue, about a year ago. Based on the thread here: How to generate video preview thumbnails for use in VideoJS? I finally concluded to go with offline generation of thumbnails, as it's much easier than trying to extract them on-the-fly.
I did a technical discussion/explanation of that struggle here: http://weasel.firmfriends.us/GeeksHomePages/subj-video-and-audio.html#implementing-video-thumbnails
My prototype example is here: https://weasel.firmfriends.us/Private3-BB/
EDIT:Also, I couldn't solve how to bind to the existing seekBar in video-js, so I added my own dedicated slider to view the thumbnails. That decision was mostly based on the need to use 'hover'/'onMouseOver' if one wants to use video-js's seekbar, and those gestures don't translate well to touch-screens (mobile devices).
EDIT: I've now solved the issue of how to bind the existing seekBar, so
I've added that logic to my prototype example mentioned above.
Cheers. Hope this helps.
For anyone whom is looking for an Angular solution. I have published a npm package for you to create a thumbnail snapshot when video progress bar is on hover.
npm: ngx-thumbnail-video
You can install it by
npm i ngx-thumbnail-video
and include it into your module:
import { NgxThumbnailVideoModule } from 'ngx-thumbnail-video';
#NgModule({
imports: [NgxThumbnailVideoModule]
})
And use it in this way:
<ngx-thumbnail-video url="assets/video.mp4" [options]="options"></ngx-thumbnail-video>
What it looks like:

How to wait for the multiple images to load in the canvas and convert to base64?

I'm trying to get a base64 version of a canvas in HTML5.
Currently, the base64 image that I get from the canvas is blank.
I have found similar questions for example this one:
HTML Canvas image to Base64 problem
However, the issue that I have is that because I am adding 2 images in the canvas, I cannot use the example provided in those answers as most of them are using single image.
I understand that I have to wait until the canvas image is loaded properly before trying to get the base64 image. But I don't know how in my case.
This is my code:
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
context.canvas.width = 1000;
context.canvas.height = 1000;
var imageObj1 = new Image();
imageObj1.src = "http://www.offthegridnews.com/wp-content/uploads/2017/01/selfie-psuDOTedu.jpg";
imageObj1.onload = function() {
context.drawImage(imageObj1, 0, 180, canvas.width, canvas.height);
};
var imageObj2 = new Image();
imageObj2.src = "http://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg"
imageObj2.onload = function() {
context.drawImage(imageObj2, 0, 0, canvas.width, 180);
};
// get png data url
//var pngUrl = canvas.toDataURL();
var pngUrl = canvas.toDataURL('image/png');
// get jpeg data url
var jpegUrl = canvas.toDataURL('image/jpeg');
$('#base').val(pngUrl);
<div class="contents" style="overflow-x:hidden; overflow-y:scroll;">
<div style="width:100%; height:90%;">
<canvas id="myCanvas" class="snap" style="width:100%; height:100%;" onclick="takephoto()"></canvas>
</div>
</div>
<p>
This is the base64 image
</p>
<textarea id="base">
</textarea>
and this is a working FIDDLE:
https://jsfiddle.net/3p3e6Ldu/1/
Can someone please advice on this issue?
Thanks in advance.
EDIT:
As suggested in the comments bellow, i tried to use a counter and when the counter reaches a specific number, I convert the canvas to base64.
Like so:https://jsfiddle.net/3p3e6Ldu/4/
In both your examples (the one from the question and the one from the comments), the order of commands does not really respect the async nature of the task at hand.
In your later example, you should put the if( count == 2 ) block inside the onload callbacks to make it work.
However, even then you will run into the next problem: You are loading the images from different domains. You can still draw them (either into the canvas or using an <img> tag), but you are not able to access their contents directly. Not even with the detour of using the <canvas> element.
I changed to code so it would work, if the images are hosted on the same domain. I also used a function to load the image and promises to handle the callbacks. The direct way of using callbacks and a counting variable, seem error-prone to me. If you check out the respective fiddle, you will notice the SecurityError shown. This is the result of the aforementioned problem with the Same-Origin-Policy I mentioned.
A previous question of mine in a similar direction was about how to detect, if I can still read the contents of a <canvas> after adding some images.
const canvas = document.getElementById('myCanvas');
const context = canvas.getContext('2d');
context.canvas.width = 1000;
context.canvas.height = 1000;
// function to retrieve an image
function loadImage(url) {
return new Promise((fulfill, reject) => {
let imageObj = new Image();
imageObj.onload = () => fulfill(imageObj);
imageObj.src = url;
});
}
// get images
Promise.all([
loadImage("http://www.offthegridnews.com/wp-content/uploads/2017/01/selfie-psuDOTedu.jpg"),
loadImage("http://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg"),
])
.then((images) => {
// draw images to canvas
context.drawImage(images[0], 0, 180, canvas.width, canvas.height);
context.drawImage(images[1], 0, 0, canvas.width, 180);
// export to png/jpg
const pngUrl = canvas.toDataURL('image/png');
const jpegUrl = canvas.toDataURL('image/jpeg');
// show in textarea
$('#base').val(pngUrl);
})
.catch( (e) => alert(e) );

Multiple images not load on canvas

I have created Circle using arc like following way.
var startAngle = 0;
var arc = Math.PI / 6;
var ctx;
var leftValue=275;
var topValue=300;
var wheelImg = ["http://i.stack.imgur.com/wwXlF.png","http://i.stack.imgur.com/wwXlF.png","cat.png","cock.png",
"croco.png","dog.png","eagle.png","elephant.png","lion.png",
"monkey.png","rabbit.png","sheep.png"];
function drawWheelImg()
{
var canvas = document.getElementById("canvas");
if(canvas.getContext)
{
var outsideRadius = 260;
var textRadius = 217;
var insideRadius = 202;
ctx = canvas.getContext("2d");
for(var i = 0; i < 12; i++)
{
var angle = startAngle + i * arc;
ctx.beginPath();
ctx.arc(leftValue, topValue, outsideRadius, angle, angle + arc, false);
ctx.arc(leftValue, topValue, insideRadius, angle + arc, angle, true);
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.arc(leftValue, topValue, outsideRadius, angle, angle + arc, false);
ctx.shadowBlur=3;
ctx.shadowColor="#A47C15";
ctx.stroke();
ctx.closePath();
ctx.save();
ctx.translate(leftValue + Math.cos(angle + arc / 2) * textRadius,
topValue + Math.sin(angle + arc / 2) * textRadius);
ctx.rotate(angle + arc / 2 + Math.PI / 2);
var img = new Image();
img.src = wheelImg[i];
ctx.drawImage(img,-44, -25,50,40);
ctx.restore();
}
}
}
function spin()
{
drawWheelImg();
}
drawWheelImg();
<button class="btnSpin" onclick="spin();">Spin</button>
<canvas id="canvas" width="550" height="580"></canvas>
Problem is that it will not load when first page load. But when i click on spin button it will load all images.
Don't know what is the issue. Any help is greatly appreciated.
Note: Here in the question same issue is solve by img.onload function but that for only single image. If there is number of multiple images in array then it's not working.
You want to preload the images.
To do this you can start the loading at the bottom of the page just before the closing </dody> tag.
<script>
// an array of image URLs
var imageNames = ["image1.png", "image2.jpg", ...moreImageNames];
// an array of images
var images = [];
// for each image name create the image and put it into the images array
imageNames.forEach(function(name){
image = new Image(); // create image
image.src = name; // set the src
images.push(image); // push it onto the image array
});
</script>
In the code that uses the images you just need to check if they have loaded yet. To do this just check the image complete attribute.
// draw the first image in the array
if(images[0].complete){ // has it loaded
ctx.drawImage(images[0],0,0); // yes so draw it
}
Note that complete does not mean loaded. If you proved a bad URL or there is another error the complete flag will still be true. Complete means the browser has finished dealing with the image. You will have to add an onload event if there is a chance that images may fail.
Handling Errors
If you are unsure that an image will not load you will have to devise a strategy to deal with the missing content. You will need to answer questions like, Can my app function without the image? Are there alternative sources to get the image from? Is there a way to determine how often this is likely to happen? How do I stop this from happening?
On the simplest level you can flag an image as loaded by adding your own semaphore to the image object during the onload event
// an array of image URLs
var imageNames = ["image1.png", "image2.jpg", ...moreImageNames];
// an array of images
var images = [];
// function to flag image as loaded
function loaded(){
this.loaded = true;
}
// for each image name create the image and put it into the images array
imageNames.forEach(function(name){
image = new Image(); // create image
image.src = name; // set the src
image.onload = loaded; // add the image on load event
images.push(image); // push it onto the image array
});
Then in code you will check for the loaded semaphore before using the image.
// draw the first image in the array
if(images[0].loaded){ // has it loaded
ctx.drawImage(images[0],0,0); // yes so draw it
}
Critical content
If you have images that are required for you app to function then you should redirect to an error page if there is a problem loading the image. You may have several servers so you can also try the different sources before giving up.
If you need to stop the app or try an alternative URL you will have to intercept the onerror image event.
To simplify your use of the images (100% sure the images are loaded when that app runs) you should start the parts that use the images only when all the images are loaded. One way to do this is to count all the images that are being loaded, and count down as the images load. When the counter reaches zero you know all the images have loaded and you can then call you app.
The following will load images, if they fail it will try another server (source) until there are no more options at which point you should redirect to the appropriate error page to inform the client that a monkey has put a spanner in the works. It counts the loading images and when all the images have loaded then it will start the app running, sure in the knowledge that all the image content is safe to use.
// Server list in order of preferance
var servers = ["https://fakesiteABC.com/", "http://boogusplace.com/", "http://foobarpoo.com.au/"];
// an array of image URLs
var imageNames = ["image1.png", "image2.jpg", ...moreImageNames];
// an array of images
var images = [];
// loading count tracks the number of images that are being loaded
var loadingImageCount = 0;
// function to track loaded images
function loaded(){
loadingImageCount -= 1;
if(loadingImageCount === 0){
// call your application start all images have loaded
appStart();
}
}
// function to deal with error
function loadError(){ // the keyword "this" references the image that generated the event.
if(this.retry === undefined){ // is this the first error
this.retry = 1; // point to the second server
}else{
this.retry += 1; // on each error keep trying servers (locations)
}
// are the any other sources to try?
if(this.retry >= servers.length){ // no 11
// redirect to error page.
return;
}
// try a new server by setting the source and stripping the
// last server name from the previous src URL
this.src = servers[this.retry] + this.src.replace( servers[ this.retry - 1],"");
// now wait for load or error
}
// for each image name create the image and put it into the images array
imageNames.forEach(function(name){
image = new Image(); // create image
image.src = servers[0] + name; // set the src from the first server
image.onload = loaded; // add the image on load event
image.onerror = loadError; // add the image error handler
images.push(image); // push it onto the image array
loadingImageCount += 1; // count the images being loaded.
});
There are many other strategies for dealing with missing content. This just shows some of the mechanism used and does not define a perfect solution (there is none)

JS: access image height after pulling it from an array

I've populated an array of images as seen here
$($target + " img").each(function() {
images.push(this);
});
I then want to access the first image in this array, specifically it's height
this.begin = function() {
var first = images[0];
};
However, when I try to access first.height and/or first.clientHeight, it's 0.
When I console.log(first); I get: <img src="img/1.jpg" class="sluider-img">.
When I console.log(images) I get: [img.sluider-img, img.sluider-img, img.sluider-img, img.sluider-img].
I can see that the target has a height / clientHeight by looking through the array seen in the above line.
Any reason I can't access the height of this element when I fetch it from the array, however I can see it while it's in the array?
I'm trying to access the height as follows
this.begin = function() {
var first = images[0];
console.log(first.clientHeight);
};
If so, how? Cheers guys.
EDIT:
When I console.log images array:
... className: "sluider-img"clientHeight: 647clientLeft: 0clientTop: 0clientWidth: 1478
... // repeat for each image element
When I console.log images[0]:
<img src="img/1.jpg" class="sluider-img">
When I do the following:
images.forEach(function(image) {
console.log(image);
});
>>
<img src="img/1.jpg" class="sluider-img">
<img src="img/2.jpg" class="sluider-img">
<img src="img/3.jpg" class="sluider-img">
<img src="img/4.jpg" class="sluider-img">
When I do the following:
images.forEach(function(image) {
console.log(image);
});
console.log(images); // this is new?
>>
sluider.js:37 img.sluider-img
sluider.js:37 img.sluider-img
sluider.js:37 img.sluider-img
sluider.js:37 img.sluider-img (it gets the image classes)
My full code
images = [];
prepare_image = function(image) {
var img = document.createElement("img");
img.setAttribute("src", image);
img.setAttribute("class", "sluider-img");
element.appendChild(img);
};
this.initialize = function(settings) {
/* creates the images and adds them into the
sluider container */
settings.images.forEach(function(image) {
this.prepare_image(image);
});
$(".sluider-img").each(function() {
images.push(this);
});
};
this.begin = function() {
/* at this point I need to get the height
of the tallest image so I can base the
container off of that. */
console.log(images);
};
And
var sluider = $($target).sluider().data($plugin);
sluider.initialize(
{
title: "TITLE",
target: $target,
images: [
"img/1.jpg",
"img/2.jpg",
"img/3.jpg",
"img/4.jpg"
]
}
);
sluider.begin();
How about checking if image was loaded?
var first = images[0];
if(first.complete){
console.log(first.height);
}else{
first.onload=function(){console.log(first.height);}
}
I do not know the EXACT scenario, but here is my answer based on understanding.
var images = [];
$.each($('.sluider-img'), function(i,d){
images.push(d);
});
var first = images[0];
var height = first.clientHeight;
alert(height);
See it working here
Basically, what you are doing is right, it is just that maybe you are not getting the value itself because of the way you are accessing the array and the value that you need (maybe because of the function that you created or you are not calling function begin)
I tried to do the same and i obtain some interesting results:
var images = [];
$('.sluider-img img').each(function(){
images.push(this)
});
console.log(images[0]);
console.log(images[0].clientHeight + " " + $(images[0]).height());
By executing the same code 20 times on Chrome I obtain, on the same image, sometimes:
0 0 (wrong)
others:
220 220 (correct heights)
Images are loaded and the code results are correct or wrong in random order.
Unfortunately I can't explain the source of this problem, but probably is the source of your frustration..
(sorry, I meant to comment but I can't because of low reputation)

Categories

Resources