I think I'm missing an obvious rule here and it would be great to get it clarified -
I'm trying to write an image preloader and I'm following the advice given on this posting:
Preloading images in Javascript? Without jQuery
I can see how it all works up until:
for(var i = 0; i < imageList.length; i++ ) {
var imageObject = new Image();
imageObject.src = imageList[i]; }
I keep thinking this would simply change/overwrite the src property at every iteration, leaving imageObject.src as the last image in the list at the end of the loop, though clearly the function is meant for using multiple images.
So I'm assuming it leaves imageObject containing an array of images but I'm not sure how it does this. What do I have wrong?
What you have will "probably" work. But, it's not the ideal way to do things because it relies on some undocumented aspects of a browser and garbage collection. See here and here for safer ways to do this. I'll try to explain what your code is doing.
When you do this line:
var imageObject = new Image();
it is creating a new DOM image object and placing a reference to that object in the variable imageObject.
When you do this line:
imageObject.src = imageList[i];
it is assigning the .src property of that new image object. This will NOT be overwriting the .src property on the previous image object.
But, because the previous image objects are not stored anywhere and no other javascript has any references to them, they are available for garbage collection by the browser and it is free to get rid of them at any time. To use this for reliable caching, you are hoping that the browser does not cancel the networking operation in progress that is loading the images and you are hoping that they still get into the browser cache so they are preloaded.
It is much safer to store the imageObject in an array as the other solutions I've pointed you to will do. This keeps them from being garbage collected so there is no risk that their image loading will be cancelled.
For example, it would be safer to do this:
var imgs = [];
for (var i = 0; i < imageList.length; i++ ) {
var imageObject = new Image();
imageObject.src = imageList[i];
imgs.push(imageObject);
}
The previous two references to other solutions to this do something similar, but package this in a function and one also has the ability to notify you when all the images have finished preloading.
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript">
var imageList = new Array("dummyImg1.jpg", "dummyImg2.jpg");
var imagePlaceholder = new Array(imageList.length);
function waitImagesLoaded() {
var allImagesLoaded = true;
for (var i = 0; i < imageList.length && allImagesLoaded; i++) {
allImagesLoaded &= imagePlaceholder[i].complete;
}
if (allImagesLoaded) {
for (var i = 0; i < imageList.length; i++) {
var imgElemIdToReplace = "img" + String(i + 1);
replaceElem(imagePlaceholder[i], document.getElementById(imgElemIdToReplace));
}
} else {
window.setTimeout(waitImagesLoaded, 500);
}
}
function replaceElem(substituteElem, elemToReplace) {
var parentNode = elemToReplace.parentNode;
parentNode.replaceChild(substituteElem, elemToReplace);
}
</script>
</head>
<body>
<img id="img1" src="" alt="">
<img id="img2" src="" alt="">
<script type = "text/javascript">
for (var i = 0; i < imageList.length; i++) {
var imageObject = new Image();
imageObject.src = imageList[i];
imagePlaceholder[i] = imageObject;
}
window.setTimeout(waitImagesLoaded, 500);
</script>
</body>
</html>
Related
I'm new to JS, and reading Javascript Dom, I'm trying to figure out one of the example in my book!
here is my html code
<!DOCTYPE html>
<html lang ="en">
<head>
<meta charset="utf-8" />
<title>Image Gallery</title>
</head>
<body>
<script type = "text/javascript" src="showPic.js"></script>
<h1>Snapshots</h1>
<ul id = "image">
<li>
Fall
</li>
<li>
Sunshine
</li>
<li>
Green
</li>
<li>
Filter
</li>
</ul>
<img id = "placeholeder" src="images/rise.jpg" alt = "my image gallery"/>
<p id="description"> Choose an image</p>
</body>
</html>
here is my javaScript code
function showPic(whichPic) {
var source = whichPic.getAttribute("href");
var placeholder = document.getElementById("placeholeder");
placeholder.setAttribute("src", source);
description.firstChild.nodeValue = text;
}
var text = whichPic.getAttribute("title");
var description = document.getElementById("description");
function perpareGallery() {
var gallery = document.getElementById("image");
var links = gallery.getElementsByTagName("a");
for(var i = 0 ; i<links.length; i++) {
links[i].onclick = function() {
showPic(this);
return false;
}
}
}
my code didnt getting anything from id = image. and I checked many times dont know what is wrong....
There seems to be no code that actually runs the perpareGallery() function.
After you fix that, it might still not work if you run perpareGallery() before the elements in HTML (like your a elements) are parsed and rendered. So, you might want to do something like:
window.onload = prepareGalery;
Although there are way better ways to set-up events, this will run your function after all HTML is parsed.
Two advices on how to track down your issue:
1) All browsers have a so called javascript console. It shows you all syntax error with your code. For firefox and chromium the keyboard shortcut F12 turns it on/off
If you run your code and open the javascript console you'll see this error:
ReferenceError: whichPic is not defined
In your example it looks as if the line var text = whichPic.getAttribute("title"); belongs to the function showPic(whichPic), but it doesn't.
2) You should format your code to not get lost. Your current code formatted:
function showPic(whichPic) {
var source = whichPic.getAttribute("href");
var placeholder = document.getElementById("placeholeder");
placeholder.setAttribute("src", source);
description.firstChild.nodeValue = text;
}
var text = whichPic.getAttribute("title");
var description = document.getElementById("description");
function perpareGallery() {
var gallery = document.getElementById("image");
var links = gallery.getElementsByTagName("a");
for(var i = 0 ; i<links.length; i++) {
links[i].onclick = function() {
showPic(this);
return false;
}
}
}
If you now take a look to the formatted code, it no longer looks as if whichPic is used inside of showPic(whichPic) but outside of the scope. The error makes perfectly sense.
You might want to move it inside of the function, before you use it.
Can images be preemptively loaded into the page with javascript so that they can be used at any time as a CSS background image without any request/upload delay?
If so, how?
You don't even need to use JS for this (with the downside of delaying the page load event). Include something like this:
<img src="/path/to/image.jpg.png.gif.bmp" style="display: none" />
This will trigger a request for the image, and add it to the local cache. When you set the CSS background-image property, the image will already be in the local cache, eliminating the delay of another request.
Alternatively, you can accomplish the same thing without delaying the page load by creating the images in JavaScript (this solution allows for multiple images):
function preload(list, callback, imageCallback) {
var at, len;
at = len = list.length;
for (var i = 0; i < len; i++ ) {
var img = new Image();
img.onload = function() {
if( imageCallback ) {
imageCallback.call(this, this, len-at, len);
}
if( !--at ) {
callback(list);
}
};
img.src = list[i];
list[i] = img;
}
}
You'd call this with:
var list = preload(["1.png","2.png","3.png" ... ], function complete(list) {
console.log('images all loaded!');
}, function loaded(image, index, listCount) {
console.log('image ' + index + ' of + 'listCount + 'is loaded');
});
(Thanks to #rlemon for the preload code)
I don't think that using an hidden img tag is the correct way, i'd rather use an "new Img(url)" and attaching to it an onload event where you can set the image as background-image to the element you want.
img = new Image();
img.onload = function(){
// set background-image
};
img.src = image_url;
be sure to put img.src after attaching onload, or you risk that the image is loaded before the event is attached.
Maybe a more complete base to build on:
function preload(list, callback, imageCallback, errorCallback) {
if (typeof(list) === "undefined"
|| list.length === 0) {
return;
}
var len = list.length;
var timers = {};
var checkLen0 = function() {
if (len === 0) {
if (typeof(callback) === "function") {
callback();
}
delete(timers)
}
}
var onload = function() {
clearTimeout(timers[img]);
if (typeof(imageCallback) === "function") {
imageCallback.call(img);
}
len--;
checkLen0();
}
var onerror = function() {
clearTimeout(timers[img]);
if (typeof(errorCallback) === "function") {
errorCallback.call(img);
}
len--;
checkLen0();
}
for (var i = 0; i < list.length; i++ ) {
var img = new Image();
img.onload = onload;
timers[img] = window.setTimeout(5000, onerror);
img.src = list[i];
}
}
While SomeKittens answer is valid, it'll delay the page load as commented by Jimmy. If you are using jquery, I'd go with something like this instead to keep your style, structure and logic separated:
<style>
.preload-img { display: none; }
</style>
...
<div class = "preload-img">/path/to/image.jpg.png.gif.bmp</div>
...
<script>
$(document).ready(function(){
$(".preload-img").each(function(){
preloadImage = new Image();
preloadImage.src = $(this).html();
});
});
</script>
Of course, from there on you can optimize/change it. The advantadge of this is that you can create the <div> dynamically with PHP and you can have all your javascript cached properly as a separated file.
There is an excellent framework for this job called Emerge.js
http://ilyabirman.net/projects/emerge/
Quote from project page:
Emerge.js is a framework for coordinated page loading. Normally, when a complex web page is loading, images appear in random order, causing unpleasant flashing. To replace it with nice and coordinated animations, programming is required. Emerge.js simplifies the task by removing the need to write any Javascript code. The framework uses a declarative approach, where you specify a desired behavior for each element and do not think about the implementation. Emerge.js uses jQuery.
Just as a caveat to SomeKittens' answer above, a particularly large background image should probably be added as a hidden background / via JavaScript after page load, as content images delay the firing of window.onload and may therefore create the perception of a slow loading page.
Given that it sounds like you're using dynamic content anyway, this may be an acceptable solution. It also allows you to do preloading programmatically as required, which may be better for maintenance.
I'm trying to display one image in loop. Knowing the path and image-name are okay is this example, how to display one image in loop, and when the image haven't been found, the browser displays the last right image-name until the image-name is found?
#{int j=1;}
<img src="" />
<script>
(function () {
for (var i = 1; true; i++) {
#{ string file = "/MonitoringN/../bitmaps/" + j + ".png"; bool a = System.IO.File.Exists(file) == true; }
var str = "/MonitoringN/../bitmaps/" + i + ".png";
var b = "#a";
if (b)
{
setInterval(function () { $('img').prop('src', str); }, 1000);
} else {
i--;
#{j--;}
}
#{j++;}
}
});
</script>
Because when I execute this code, I get a blank image, and then I can't see the page is loading.
Thanks a lot!
I think I know what you are trying to do ...
If I understand the question correctly, you have a list of images and you want to try to open them until one of them is found. If an image is NOT found, you want to skip to the next one.
First -- I'd simply for your question by separating the Razor stuff and the Javascript off into very separate pieces. In fact, I'm going to skip Razor entirely.
<html>
<head>
<title>A test</title>
</head>
<body>
<img src="http://x.invalid" id="myImage">
<script>
var imgs = [
"http://x1.invalid/none",
"https://www.google.com/images/srpr/logo11w.png",
"http://thedailywtf.com/Resources/Images/Primary/logo.gif"
];
var imageIndex = 0;
function tryNextImage() {
var img = document.getElementById("myImage");
img.onerror = function() {
imageIndex++;
tryNextImage();
}
img.src = imgs[imageIndex];
}
// start the ball rolling
tryNextImage();
</script>
</body>
</html>
So far I created an array with 11 images, initialized the counter, created a function, created a for loop but here is where I get lost. I looked at examples and tutorial on the internet and I can see the code is seeming simple but I'm not getting something basic here. I don't actually understand how to call the index for the images. Any suggestions. Here is the code.
<script type="text/javascript">
var hammer=new Array("jackhammer0.gif",
"jackhammer1.gif",
"jackhammer2.gif",
"jackhammer3.gif",
"jackhammer4.gif",
"jackhammer5.gif",
"jackhammer6.gif",
"jackhammer7.gif",
"jackhammer8.gif",
"jackhammer9.gif",
"jackhammer10.gif")
var curHammer=0;
var numImg = 11;
function getHammer() {
for (i = 0; i < hammer.length; i++)
{
if (curHammer < hammer.length - 1) {
curHammer = curHammer +1;
hammer[i] = new Image();
hammer[i].src="poses/jackhammer" +(i+1) + ".gif";
var nextHammer = curHammer + 1;
nextHammer=0;
{
}
}
}
}
setTimeout("getHammer()", 5000);
</script>
</head>
<body onload = "getHammer()";>
<img id="jack" name="jack" src = "poses/jackhammer0.gif" width= "100" height ="113" alt = "Man and Jackhammer" /><br/>
<button id="jack" name="jack" onclick="getHammer()">Press button</button>
Following on what Paul, said, here's an example of what should work:
var hammer=["jackhammer0.gif","jackhammer1.gif","jackhammer2.gif","jackhammer3.gif",
"jackhammer4.gif","jackhammer5.gif","jackhammer6.gif","jackhammer7.gif",
"jackhammer8.gif","jackhammer9.gif","jackhammer10.gif"];
var curHammer=0;
function getHammer() {
if (curHammer < hammer.length) {
document.getElementById("jack").src= "poses/" + hammer[curHammer];
curHammer = curHammer + 1;
}
}
setTimeout("getHammer()", 5000);
The big missing element is that you need to call getElementById("jack") to get a reference to the DOM Image so that you can change it's source. If you're using jQuery or most other JS frameworks, just type $("#jack") to accomplish the same.
I don't understand the need for the for loop at all, just increment the index value [curHammer] each time you click, and reset if it passes your max index length (in this case 11).
Pseudo-Code:
currentHammer = -1
hammers = [ "a1.jpg", "a2.jpg", "a3.jpg"]
getHammer()
{
currentHammer = currentHammer + 1;
if(currentHammer > 2)
currentHammer = 0;
image.src = hammers[currentHammer];
}
a) are you just trying to show an animated gif? If so, why not use Adobe's Fireworks and merge all those gifs into a single gif?
b) you know that the way you have it the display is going to go crazy overwriting the gif in a circle right?
c) you might want to put a delay (or not). If so, make the load new gif a separate function and set a timeout to it (or an interval).
Also, you are being redundant. How about just changing the src for the image being displayed?:
var jackHammer = new Array();
for (var i=0;i<11;i++) { //pre-loading the images
jackHammer[i] = new image();
jackHammer[i].src = '/poses/jackHammer'+i.toString()+'.gif';
} //remember that "poses" without the "/" will only work if that folder is under the current called page.
for (var i=0;i<11;i++) { //updating the image on
document.getElementById('jhPoses').src = jackHammer[i].src;
}
on the document itself,
< img id='jhPoses' src='1-pixel-transparent.gif' width='x' height='y' alt='poses' border='0' />
I test the code in IE7, FF, Chrome, Safari and this problem occurs in Firefox only.
I have checked that the problem only occurs in FF 3.5.x, but not FF 3.0.x.
I want to make an image animation which has 10 images in total.
Currently, I use the following code to do it:
for (var i=1;i<=10;i++){
img[i] = new Image();
img[i].src = "images/survey/share_f"+i+".jpg"
}
var cornerno = 0;
function imganimate(){
$("#surveyicon").attr("src",img[cornerno].src);
//some logic to change the cornerno
setTimeout("imganimate()",1000);
}
And then change an element's src to loop through the array "img".
however, firefox keeps requesting the images continuous (I expect it only requests each unique image just once).
What should I do?
img is undefined. just add a line "var img = new Array();" before "for (var i=1;i<=10;i++){"
var img = new Array();
for (var i=1;i<=10;i++){
img[i] = new Image();
img[i].src = "images/survey/share_f"+i+".jpg";
}
var cornerno = 0;
function imganimate(){
cornerno %= 10;
cornerno++;
$("#surveyicon").attr("src",img[cornerno].src);
setTimeout("imganimate()",1000);
}
imganimate();
Try composing the images into a single image file like a sprite map and then use CSS positioning to shift the image around as a background image. It will be much faster and avoid any reloads.
You've already created ten DOM nodes, set their src and loaded the images. Why would you set the src again? You want to rotate those ten nodes in and out now. You can either toggle style.display or remove and insert the nodes.
Here's my suggestion. I'm not well versed in JQuery so there may be a few additional shortcuts I've overlooked:
var imgAmt = 10;
img = [];
for (var i=1;i<=imgAmt;i++){
img[i] = document.createElement("img");
img[i].src = "images/survey/share_f"+i+".jpg"
img[i].style.display = "none";
$("#surveyicon").appendChild(img[i]);
}
imganimate();
var cornerno = 0;
function imganimate(){
cornerno++;
cornerno = cornerno > imgAmt ? 1 : cornerno;
for (var i=1;i<=imgAmt;i++){
// hide all images but the index that matches cornerno:
img[i].style.display = i==cornerno ? "" : "none";
}
setTimeout(imganimate,1000);
}
This seems a bug in FF3.5.x.
Not sure whether the bug has already been fixed.