I have a javascript that creates several div elements and inside them an img tag for each of several image urls. I also have a timeout function that after a period is supposed to cause the image to stop loading. In FF this can be achieved by setting the img tag's src attribute to be blank. In webkit browsers it's a different story since all requests are made asynchronously.
The code I have so far looks similar to this:
function tag(url_array, timeout)
{
var _tag_div = document.getElementById('tag_div');
for(var i in url_array)
{
var e = document.createElement('div');
e.setAttribute('id', '_tag_' + i);
e.innerHTML = '<img src="' + urls[i] + '"/>';
_tag_div.appendChild(e);
setTimeout(blank_img.bind(window, i), timeout);
}
function blank_img(x)
{
document.getElementById('_tag_' + x).firstChild.src = '';
}
}
As I said this works well in FF but in webkit browsers it does nothing to stop the loading of the images. Given that this is going to be used for a pixel tracking system on 3rd party pages the code needs to be as lightweight as possible. Does anybody know of a solution to this problem or can maybe assist me in finding one? Thanks in advance,
-CarbonX
Try removing them from the DOM completely with removeChild, e.g.:
function blank_img(x) {
var imgTag = document.getElementById('_tag_' + x);
if(imgTag) { imgTag.parentNode.removeChild(imgTag); }
}
I'm assuming you don't need the tags later...
Related
As I mentioned in the title I have a problem properly embedding my animation in my Django project.
At some point, I decided to update the logo used by my web page built-in Django.
Having small experience with JavaScript I was looking for approaches that fit my needs.
I built an animation engine having some inspiration from this StackOverflow answer. (Thanks to user gilly3).
My approach is, though, different in this topic because I realised that if I would have a lot of pictures then sticking/concatenating them together in one file might be difficult. I've decided to use another approach and use a bunch of files instead of one. For that sake, I built a function with a generator which I could call in another function to display all pictures in order. This animation engine looks like this:
function* indexGenerator() {
let index = 1;
while (index < 28) {
yield index++;
}
if (index = 28)
yield* indexGenerator();
};
var number = indexGenerator();
setInterval(function animationslider()
{
var file = "<img src=\"../../../static/my_project/logos/media/slides_banner_f/slide_" + number.next().value + ".jpg\" />";
document.getElementById("animation-slider").innerHTML = file;
$("#animation-slider").fadeIn(1000);
}, 100);
$("#animation-slider").fadeIn(1000); doesn't do the trick with values from 10-1000.
I record what this looks like:
https://youtu.be/RVBtLbBArh0
I suspect that the solution to the first relocation problem can be solved using CSS, but I don't know how to do it, yet.
The blinking/flickering of the animation is probably caused by loading all these images one by one. In the example video above, there are 28 of them. I wonder if this content would be loaded asynchronously (e.g. using Ajax) would it solve the problem? Is it possible to load all and after all is loaded, then display the animation (some sort of preloading)? I will be grateful for all suggestions and hints.
I'm aware that in the topic mentioned by me before there is a solution. But I'm curious about other approaches.
At the moment, from what I checked, the best method would be to convert pictures to the format:
APNG
WebP which seems to be better quality.
GIF
Having that kind of file, there is no problem with loading and flickering.
The flickering is the time it takes to create the dom element, insert it into your page, make the web request to download the image, and final render the image. That's too much work to do in the moment you want to animate the next frame! It would look even worse running from a client with a poor connection to the server.
To solve this, preload all of the images. This is the benefit of a sprite - it loads all at once by its very nature. But, if you have individual images, you can still preload them. Just hide them all at first, and show them one by one.
One way you can show them one at a time, is to set up a style that hides all but the first child image. Then, to animate, move the first image to the end of the list.
#animation-slider img ~ img {
display: none;
}
const animationContainer = document.getElementById("animation-slider");
for (let i = 0; i < 28; i++) {
const img = document.createElement("img");
img.src = `../../../static/my_project/logos/media/slides_banner_f/slide_${i}.jpg`;
animationContainer.appendChild(img);
}
addEventListener("load", () => {
setInterval(() => animationContainer.appendChild(animationContainer.children[0]), 100);
});
Here's a working example, using my own image:
const animationContainer = document.getElementById("animation-slider");
for (let i = 1; i < 16; i++) {
const img = document.createElement("img");
img.src = `https://jumpingfishes.com/dancingpurpleunicorns/charging${i.toString().padStart(2, "0")}.png`;
animationContainer.appendChild(img);
}
addEventListener("load", () => {
setInterval(() => animationContainer.appendChild(animationContainer.children[0]), 100);
});
#animation-slider img ~ img {
display: none;
}
<div id="animation-slider"></div>
function displayInfo(nKey) {
var i, xdata, xfilm, xprop;
if(!nKey) return false;
var objFilm;
var imgRef;
//iterate through all object properties; display their attributes
var jqxhr = $.getJSON('dbMovies.json', function(data) {
$.each(data.disc, function(i, xdata) {
if(xdata.key == nKey) {
objFilm = xdata.film;
imgRef = xdata.img;
if(xdata.doc) bkDoc = true;
return false;
}
}); // $.each
})
.done(function() {
// objFilm has either a single film object or else an array of film objects
var arInfo = [];
if(!$.isArray(objFilm)) {
var arProps = Object.keys(objFilm);....//absolutely
arProps.forEach(function(item) {
if(item != "ref") {
arInfo.push(item + ": " + objFilm[item] + "<br>");
} else {
arInfo.push(item + ": <a href=" + objFilm[item] + " target=_blank>Wikipedia</a>");
}
});
var w = window.open('', '', 'width = 650, height = 500, resizable');
$(w.document.body).html(arInfo.join(""));
}) // .done
I have what we'll call a kiosk app that contains the contents of my film library as stored in a JSON file. One can access the contents in several ways, but each yields a display of all relevant titles. For example, searching for films with Sophia Loren, one would see this result:
All browsers work to this point.
You can see that each film has a link that leads to certain information about the film.
Although one browser (Mac Safari) reportedly does not display the Wikipedia link, all other browsers do. But only the Microsoft browsers (Edge, IE11) show the associated thumbnail. Therefore(?), only the Microsoft browsers respond to a click by invoking my display engine on the full version of the image. All browsers respond well and equally on more direct invocations of the display engine. For example, see [http://www.michaelbroschat.com/LongviewChristmas/LongviewChristmas.html].
The information window is created dynamically upon clicking the index number link shown in the first illustration.
All browsers successfully create the new window and most of the information data items. In fact, Chrome and Firefox appear to create the image display code but don't act upon it.
The entire app can be seen at http://www.michaelbroschat.com/film/disccatalog.html
I would love to know why Chrome and Firefox don't allow what the Microsoft browsers allow.
You left out of your question a critical part of the code, that being the code that creates the <a> tag around the image reference.
The problem you're having is that you're stuffing the assembled HTML for the list of films into that popup window, which is opened with no URL reference. Your <img> tags are built with relative URLs (like "liners/i0001.jpg"), but since there's no base URL for the browser to reference the image can't be loaded.
Internet Explorer may be making some inference that the popup window has the same base URL as the parent, but other browsers apparently don't do that.
I have some theories on this portion of your code:
............arProps.forEach(function(item) {
................if(item != "ref") {
....................arInfo.push(item + ": " + objFilm[item] + "<br>");
................} else {
....................arInfo.push(item + ": <a href=" + objFilm[item] + " target=_blank>Wikipedia</a>");
................}
............});
ONE: Be sure the anchor tag is using single quotes or double quotes depending on your preference when you output the source such as:
Wikipedia
My guess is that being only your version of safari is having an issue with the Wikipedia link it is sensitive to the quotes.
As far as the image goes, where is the img tag to output an image path?
I am loading a large number of images into a dynamic DIV and I am using a preloader to get the images.
imageObj = new Image();
imageObj.src = imgpath + imgname;
Each of these events creates a GET that I can see and monitor in Firebug.
If I know the name and path of an image, can I watch the relevant XMLHttpRequest to see if the GET has completed?
I do not want to rely on (or use) .onload events for this process.
The pseudo would look something like this...
if (imageObj.GET = 'complete')
Has anyone had any experience of this?
EDIT 1
Thanks to the help from Bart (see below) I have changed my image preloader to store an array of the image objects...
function imagePreLoader(imgname) {
images[imgnum] = new Image();
images[imgnum].src = imgpath + imgname;// load the image
imgnum ++;
}
And then, after all my other functions have run to build the content DIVs, I used the image.complete attribute in the following...
var interval = setInterval(function () {
imgcount = imgnum - 1; // because the imgnum counter ++ after src is called.
ok = 1;
for (i=0; i<imgcount; i++) {
if (images[i].complete == false){
ok = 0;
}
}
if (ok == 1) {
clearInterval(interval);
showIndexOnLoad();
}
}, 1000);
This waits until all the images are complete and only triggers the showIndexOnLoad() function when I get the 'ok' from the interval function.
All images now appear as I wanted, all at once with no additional waits for the GETs to catch up.
Well done Bart for putting me on to the image.complete attribute.
You can watch the complete property of the image to see if the image is fully loaded or not.
Here's an example.
http://jsfiddle.net/t3esV/1/
function load (source) {
var img = new Image();
img.src = source;
console.log('Loading ' + source);
var interval = setInterval(function () {
if (img.complete) {
clearInterval(interval);
complete(img);
}
}, 400);
};
function complete(img) {
console.log('Loaded', img.src);
document.body.appendChild(img);
};
Note: This example fails to clear the interval when something goes wrong and complete is never set to true.
Update
I wrote a simple jQuery.preload plugin to take advantage of the image.complete property.
This is a very interesting problem, and I am afraid there is no actual solution to this. The load event for images is when the image is being rendered and the browser knows the width and height of it.
What you would be after would be a tag-applicable readystatechange event. Alas, only IE allows you to bind those to non-document elements, so this is not an option.
There are a bunch of plug-ins that allow you to go around it, as well. One pretty hot one is https://github.com/desandro/imagesloaded , which has the added advantage of dealing with all the browser differences very efficiently. It, however, still relies on the load event (and I am pretty sure this is the only way to start doing what you want to do).
I've been looking around and I'm starting to worry that this isn't possible.
Is there any way to make a standard <audio> tag with fallbacks...
<audio>
<source src='song.ogg' type='audio/ogg'></source>
<source src='song.mp3' type='audio/mp3'></source>
</audio>
...have an onload event. I've looked around and all I could find are some hacks that may or may not work (they don't for me on Chrome) and the canplaythrough event.
The reason I want this is because I am making a presentation that has lots of audio clips to play at certain points. I don't want the presentation to start until all of the audio is loaded (otherwise things could get out of sync). I want the clips to be loaded 1 at a time so that I can create a sort of loading bar. I really don't want to resort to using Flash sound because this is supposed to demonstrate pure web technologies.
So basically I've got this one loadAudio function that cycles through the array of audio files to be loaded audioQueue. loadAudio is called once and then it calls itself until all the files are loaded.
Problem is I haven't found the correct event to trigger loading the next file.
loadAudio = function(index)
{
mer = audioQueue[index];
var ob = "<audio id='" + mer + "'><source src='resources/sounds/" + mer + ".ogg' type='audio/ogg'></source><source src='resources/sounds/" + mer + ".mp3' type='audio/mp3'></source></audio>";
$("#audios").append(ob);
$("#" + mer).get(0).addEventListener('A WORKING EVENT RIGHT HERE WOULD BE NICE', function() { alert("loaded");
if (index + 1 < audioQueue) { loadAudio(index + 1); } }, false);
}
So. Any chance for a proper audio onload? I'm basically willing to do anything as long as it's still all HTML and Javascript.
You can use the loadeddata-MediaEvent. For example you can put all of your audio files in an Array and do something like:
var files = ['a.mp3', 'b.mp3'];
$.each(files, function() {
$(new Audio())
.on('loadeddata', function() {
var i = files.indexOf(this);
files.splice(i, 1);
if (!files.length) {
alert('Preloading done!');
}
})
.attr('src', this);
});
EDIT: this would a little more modern approach as of 2016:
var files = ['a.mp3','b.mp3'];
Promise
.all(files.map(function(file) {
return new Promise(function(resolve) {
var tmp = new Audio();
tmp.src = file;
tmp.addEventListener('loadeddata', resolve);
});
})).then(function() {
alert('Preloading done!');
});
I did a small PONG-game with WebGL and some audio-tags for the sounds. I borrowed the audio-implementation from Opera's Emberwind HTML5 implementation: https://github.com/operasoftware/Emberwind/blob/master/src/Audio.js
Their solution worked fine for me (Chrome, Opera and Firefox). Maybe it could be of interest to you? They have some code that will try to find a playable format from line 22 and below.
Is there a way to tell, after the fact, whether an image (placed with the <img> tag, not via JS) has loaded correctly into a page? I have a gallery of head shots, and occasionally the third-party image server ends up serving up a 404. I can change the server-side code to use an onerror="showGenericHeadshot()", but I really want to avoid making changes to server-side code. Ultimately, I want to determine if an image is missing or broken and replace it with a generic "Image Not Found" graphic. Things I've tried:
Image.prototype.onerror = showGenericHeadshot -- doesn't work for <img> tags
$('img[src*=thirdpartyserver.com]).error(showGenericHeadshot) -- doesn't work in IE
$('img[src*=thirdpartyserver.com]).css('backgroundImage','url(replacementimage.gif)') -- works, but still doesn't get rid of the broken image icon in IE
<img scr='someUrl' id="testImage" />
jQuery('#testImage').bind('load',function(){
alert ('iamge loaded');
});
to avoid race condition do as below
<img _src="http://www.caregiving.org/intcaregiving/flags/UK.gif" />
// i have added an underscore character before src
jQuery('img').each(function(){
var _elm=jQuery(this);
_elm.bind('load',_imageLoaded).attr('src',_elm.attr('_src'))
});
function _imageLoaded()
{
alert('img loaded');
}
Unfortunately, I'm not able to accept either #TJ Crowder's nor #Praveen's excellent answers, though both do perform the desired image-replacement. #Praveen's answer would require a change to the HTML (in which case I should just hook into the <img> tag's own error="" event attribute. And judging by network activity, it look like if you try to create a new image using the url of an image that just 404ed in the same page, the request actually does get sent a second time. Part of the reason the image server is failing is, at least partly, our traffic; so I really have to do everything I can to keep requests down or the problem will only get worse..
The SO question referred to in #danp's comment to my question actually had the answer for me, though it was not the accepted answer there. I'm able to confirm that it works with IE 7 & 8, FF and webkit browsers. I'm doubtful it will work with older browsers, so I've got a try/catch in there to handle any exceptions. The worse case will be that no image-replacement happens, which is no different from what happens now without doing anything. The implementation I'm using is below:
$(function() {
$('img[src*=images.3rdparty.com]').each(
function() {
try {
if (!this.complete || (!$.browser.msie && (typeof this.naturalWidth == "undefined" || this.naturalWidth == 0))) {
this.src = 'http://myserver.com/images/no_photo.gif';
}
} catch(e) {}
}
);
});
Would an alternate text be sufficient? If so you can use the alt attribute of the img tag.
I think I've got it: When the DOM is loaded (or even on the window.load event — after all, you want to do this when all images are as complete as they're going to get), you can retroactively check that the images are okay by creating one new img element, hooking its load and error events, and then cycling through grabbing the src from each of your headshots. Something like the code below (live example). That code was just dashed off, it's not production quality — for instance, you'll probably want a timeout after which if you haven't received either load or error, you assume error. (You'll probably have to replace your checker image to handle that reliably.)
This technique assumes that reusing a src does not reload the image, which I think is a fairly reliable assumption (it is certainly an easily testable one) because this technique has been used for precaching images forever.
I've tested the below on Chrome, Firefox, and Opera for Linux as well as IE6 (yes, really) and IE8 for Windows. Worked a treat.
jQuery(function($) {
var imgs, checker, index, start;
// Obviously, adjust this selector to match just your headshots
imgs = $('img');
if (imgs.length > 0) {
// Create the checker, hide it, and append it
checker = $("<img>").hide().appendTo(document.body);
// Hook it up
checker.load(imageLoaded).error(imageFailed);
// Start our loop
index = 0;
display("Verifying");
start = now();
verify();
}
function now() {
return +new Date();
}
function verify() {
if (!imgs || index >= imgs.length) {
display("Done verifying, total time = " + (now() - start) + "ms");
checker.remove();
checker = undefined;
return;
}
checker[0].src = imgs[index].src;
}
function imageLoaded() {
display("Image " + index + " loaded successfully");
++index;
verify();
}
function imageFailed() {
display("Image " + index + " failed");
++index;
verify();
}
function display(msg) {
$("<p>" + now() + ": " + msg + "</p>").appendTo(document.body);
}
});
Live example