How to empty IMG element quickly? - javascript

I've built a modal image gallery. When the user clicks next to see next image I run something like:
document.getElementById('gallery-image').src = newSRC;
My problem is that there is a delay from when the user clicks next to where the image actually changes. (due to the new image being loaded). I want to "empty" the image element the moment the user clicks next. I tried doing this:
document.getElementById('gallery-image').src = '';
setTimeout(function(){
document.getElementById('gallery-image').src = newSRC;
}, 100);
to no avail.
How can I "empty" the IMG element until the new image starts loading on it?

There are three main ways to do this. Your question seemed to suggest you want to create an effect similar to a slide projector changing slides, so I have written this answer with that in mind.
Temporarily set the src property to a 1x1 transparent GIF instead of the empty string. This is most similar to what you tried to do and perhaps is the simplest method.
The data URI below is from Blank image encoded as data-uri and should work in Internet Explorer 8 or higher and the other major browsers. Using a data URI avoids the need to store a separate file on your web server and preload it when your page loads. You may need to set the width and height attributes of the img element to preserve the layout of your page.
This code does not preload the image at the start of the time delay (although it would be possible to do so), thus the apparent delay to the viewer is the sum of the programmed time delay and the time taken to load the image. The loading time will only be close to zero if the browser has already cached the image or if the web server is on the local network.
var gi = document.getElementById('gallery-image');
gi.src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
setTimeout(function(){
document.getElementById('gallery-image').src = newSRC;
}, 100);
Temporarily set display: none;, and use an onload function to undo the change once the time delay has elapsed and the image has loaded. The code below starts loading the image at the start of the time delay, which depending on the viewer's connection speed, may result in a more consistent apparent delay.
var gi = document.getElementById('gallery-image');
var delayElapsed = false;
var imageLoaded = false;
gi.style.display = 'none';
setTimeout(function(){
delayElapsed = true;
maybeShowImage();
}, 100);
gi.onload = function() {
imageLoaded = true;
maybeShowImage();
};
gi.src = newSRC;
function maybeShowImage() {
if (!delayElapsed || !imageLoaded) {
return;
}
gi.style.display = '';
}
Do the above, but set visibility: hidden; instead. This may have the advantage of not affecting the page layout where display: none; would.
// [...]
gi.style.visibility = 'hidden';
// [...]
function maybeShowImage() {
if (!delayElapsed || !imageLoaded) {
return;
}
gi.style.visibility = '';
}

You could hide the image and then reshow it...
// Hide
document.getElementById('gallery-image').style.display = "none";
// Show
document.getElementById('gallery-image').style.display = "";

Related

Javascript: Append and Remove Image from a DIV in setTimeout loop

I'm trying to load an image into a div using JavaScript. Below is the current snippet of the code.
window.onload=function image_show() {
var thumbContainer = document.getElementById("art_img");
var thumbnail = document.createElement("img");
var img_width = document.getElementById("art_img").offsetWidth;
var img_height = document.getElementById("art_img").offsetHeight;
thumbnail.onload=function() {
thumbContainer.appendChild(thumbnail);
}
thumbnail.src = "http://xyz/bkg.png";
thumbnail.width = img_width;
thumbnail.height = img_height;
setTimeout(image_show, 1000 );
}
The appendChild() method keeps appending the images (one below the other) after the Timeout. But I want to actually keep refreshing the div with the same image.
The source of the image will be updated on my website with the same name, and I want the script to display the new image after Timeout. I tried inserting a removeChild() method just before appendChild(), but didn't work as expected. Any help will be appreciated.
just empty the element before you append again
thumbnail.onload=function() {
thumbContainer.innerHTML = ""; // ADD THIS LINE
thumbContainer.appendChild(thumbnail);
}
The problem is that the browser caches the image.
Try appending a timestamp to the src attribute of the thumbnail:
thumbnail.src = "http://xyz/bkg.png?ts=" + Date.now();
This way, the source URL will be slightly different each time the image_show function runs and the picture should be loaded by the browser each time.

dynamic image loading google chrome

I have a newbie question about running JavaScript on my website, and how images load.
Essentially, I developed a page which will generate a simple, on-screen invoice for a small business owner. The owner can go to the page, click a button, and they are asked for a variety of inputs to populate the invoice. They're asked to enter an image for their logo, a list of work they did (& what they charged), contact information at the bottom, and more.
It's primitive and I know there's many services out there that do this professionally, but I own a small business & wanted to learn HTML & JS.
My particular issue is about how the logo image displays when the user inputs the URL. The JS prompts for the logo URL after the user clicks the start button. Then the script asks the user if they're happy with the image as displayed (i.e. shape & size).
When I load the site on IE11, the image displays as soon as the user enters the URL. This way, they can see it immediately & decide if they're happy with it or not. The problem is, when I run it on Google Chrome, the image doesn't load until the entirety of the invoice-generation program has run. In other words, the image doesn't display until all the JS scripts that make up the program are executed.
Needless to say this limits my users to one attempt at displaying their logo since they don't see it on-screen until after the program is over. Why does it display immediately in IE 11 but not Chrome, and how can I get it to display immediately in Chrome (if possible)?
The link to the page is Generate Invoice
Here is the JS code that prompts for the image, displays it and re-sizes it as the user likes.
var url = prompt("Enter the URL for a picture of your logo:");
// create the IMG element to display the logo
var image = document.createElement("IMG");
// assign the user-input url to the variable holding the IMG element
image.src=url;
// Create a variable to assign the DIV to (by ID)
var imageLogo = document.getElementById("addLogoAndMessage");
// Clear out the DIV (i.e. make the button go away)
imageLogo.innerHTML = "";
// Append the image to the variable associated with the DIV
imageLogo.appendChild(image);
// custom width for CE logo is 220px
var logoWidth = prompt("Set a width for your logo.");
var logoWidth1 = logoWidth + "px";
imageLogo.appendChild(image).style.width = logoWidth1;
// custom height for CE logo is 200px
var logoHeight = prompt("Set a height for your logo. To maintain aspect ratio, just hit Enter.");
var logoHeight1 = logoHeight + "px";
if (logoHeight == "" || logoHeight == "undefined") {
imageLogo.appendChild(image).style.height = "auto";
} else {
imageLogo.appendChild(image).style.height = logoHeight1;
}
var sizeOK = prompt("Are you happy with the shape and size of your logo (y/n)?");
while (sizeOK != "n" && sizeOK != "y") {
sizeOK = prompt("Are you happy with the shape and size of your logo (y/n)?");
}
while (sizeOK == "n") {
var logoWidth = prompt("Set a new width for your logo.");
var logoWidth1 = logoWidth + "px";
imageLogo.appendChild(image).style.width = logoWidth1;
var logoHeight = prompt("Set a new height for your logo. To maintain aspect ratio, just hit Enter.");
var logoHeight1 = logoHeight + "px";
if (logoHeight == "" || logoHeight == "undefined") {
imageLogo.appendChild(image).style.height = "auto";
} else {
imageLogo.appendChild(image).style.height = logoHeight1;
}
// Ask after every width,height set so user can stop when they like the image
sizeOK = prompt("If you're still not happy with your logo, enter 'n'. Otherwise, just hit Enter.");
}
Thank you for any thoughts or input.
It could be that Chrome waits for the image to finish loading before it performs a document reflow ( = before it updates the layout). Because the JavaScript is executed much faster than the image could have possibly loaded, you won't see the image until the JS has finished.
You could try attaching an onload event to the image that fires when it has finished loading. So, use
image.onload = imageHasLoaded;
image.src = url;
And then place the rest of the code within a seperate function
function imageHasLoaded() {
//the rest of your code, to be executed once the image is displayed
}
Hope that helps!

How to properly scroll IFrame to bottom in javascript

For a mockup-webpage used for research on interaction on websites, I created a mockup message-stream using JavaScript. This message stream is loaded in an IFrame and should show images at pre-set intervals and scroll to the bottom of the page after placing a new image at the bottom of the page. Getting the images to appear is working quite well with the provided script. However, both Chrome and IE seem to have trouble scrolling the page to the bottom. I would like to scroll to the bottom of the page as soon as the image is attached, but have for now added a 5 ms delay because that seemed to work sometimes. My questions are:
Is it okay to use document.body.scrollHeight for this purpose?
Can I make the scroll occur directly, or do I need a small interval before scrolling?
How to make the code scroll to the bottom of the IFrame directly after adding an image?
The following functions are used and trypost() is started onLoad:
function scrollToBottom(){
window.scrollBy(0,document.body.scrollHeight);
}
function trypost(){
point = point + 1;
if(point < interval.length){
//create and append a new image
var newImg = document.createElement("IMG");
newImg.src = "images/"+images[point]+".png";
document.getElementById('holder').appendChild(newImg);
//create and append a return
var br = document.createElement("br");
document.getElementById('holder').appendChild(br);
//time scroll to bottom (after an arbitrary 5 seconds)
var stb = window.setTimeout(scrollToBottom, 5);
//time next post
var nextupdate = interval[point]*400;
var tp = window.setTimeout(trypost, nextupdate);
}
}
My script section contains at least the following variables:
var point = -1;
var interval = [10, 10, 15];
var images = ["r1", "a1", "r2"];
This questions is a continuation of the project described in How to proper use setTimeout with IE?
To answer one of your questions, document.body.scrollHeight is appropriate for this purpose, but not if you're actually calling for document. That'll give you the scroll height of the document the iFrame is in, not the iFrame's document. The iFrame's document can be called upon by [insert variable for iFrame here].contentDocument.
Here's how I did it (and by that, I mean I tested it out with my own stuff to make sure it worked):
let i = document.querySelector('iframe')
i.contentWindow.scrollTo(0, i.contentDocument.body.scrollHeight);
That being said, the other answer by Thomas Urban will also work most of the time. The difference is only if your page has a really long scroll height. Most pages won't be longer than 999999 (for all I know that's impossible and that's why they chose that number), but if you have a page longer than that, the method I showed here would scroll to the bottom and the 999999 would scroll to somewhere not yet at the bottom.
Also note, if you have more than one iFrame, you're gonna want to query it in a different way than I did, like by ID.
Scrolling to bottom is always like scrolling to some ridiculously large top offset, e.g. 999999.
iframe.contentWindow.scrollTo( 0, 999999 );
In addition see this post: Scrolling an iframe with javascript?
If scrolling occurs too early it's probably due to images not being loaded yet. Thus, you will have to scroll as soon as added image has been loaded rather than on having placed it. Add
newImg.onload = function() { triggerScrolling(); };
after creating newImg, but before assigning property src.
If several events are required to trigger scrolling you might need to use some "event collector".
function getEventCollector( start, trigger ) {
return function() {
if ( --start == 0 ) { trigger(); )
};
}
You can then use it like this:
var collector = getEventCollector( 2, function() { triggerScrolling(); } );
newImg.onload = collector;
window.setTimeout( collector, 100 );
This way triggerScrolling() is invoked after 100ms at least and after image has been loaded for collector has to be invoked twice for triggerScrolling() being invoked eventually.

Proper way to reset a GIF animation with display:none on Chrome

Title is self-explanatory, but I'll provide a step-by-step view on the matter. Hopefully I'm not the first one to have noticed this (apparently) bug on Webkit/Chrome.
I want to reset a GIF animation. All of the examples I've seen so far either simply set the src of the image to itself or set it to an empty string followed by the original src again.
Take a look at this JSFiddle for reference. The GIF resets perfectly fine on IE, Firefox and Chrome.
The issue which I have is when the image has display:none on Google Chrome only.
Check this JSFiddle. The GIF resets fine on IE and Firefox before being displayed in the page, but Chrome simply refuses to reset its animation!
What I've tried so far:
Setting the src to itself as in Fiddle, doesn't work in Chrome.
Setting the src to an empty string and restoring it to the default, doesn't work either.
Putting an wrapper around the image, emptying the container through .html('') and putting the image back inside of it, doesn't work either.
Changing the display of the image through .show() or .fadeIn() right before setting the src doesn't work either.
The only workaround which I've found so far is keeping the image with its default display and manipulating it through .animate()ing and .css()ing the opacity, height and visibility when necessary to simulate a display:none behaviour.
The main reason (context) of this question is that I wanted to reset an ajax loader GIF right before fading it in the page.
So my question is, is there a proper way to reset a GIF image's animation (which avoids Chrome's display:none "bug") or is it actually a bug?
(ps. You may change the GIF in the fiddles for a more appropriate/longer animation gif for testing)
The most reliable way to "reset" a GIF is by appending a random query string. However this does mean that the GIF will be redownloaded every time so make sure it's a small file.
// reset a gif:
img.src = img.src.replace(/\?.*$/,"")+"?x="+Math.random();
Chrome deals with style changes differently than other browsers.
In Chrome, when you call .show() with no argument, the element is not actually shown immediately right where you call it. Instead, Chrome queues the application of the new style for execution after evaluating the current chunk of JavaScript; whereas other browsers would apply the new style change immediately. .attr(), however, does not get queued. So you are effectively trying to set the src when the element is still not visible according to Chrome, and Chrome won't do anything about it when the original src and new src are the same.
Instead, what you need to do is to make sure jQuery sets the src after display:block is applied. You can make use of setTimeout to achieve this effect:
var src = 'http://i.imgur.com/JfkmXjG.gif';
$(document).ready(function(){
var $img = $('img');
$('#target').toggle(
function(){
var timeout = 0; // no delay
$img.show();
setTimeout(function() {
$img.attr('src', src);
}, timeout);
},
function(){
$img.hide();
}
);
});
This ensures that src is set after display:block has been applied to the element.
The reason this works is because setTimeout queues the function for execution later (however long later is), so the function is no longer considered to be part of the current "chunk" of JavaScript, and it provides a gap for Chrome to render and apply the display:block first, thus making the element visible before its src attribute is set.
Demo: http://jsfiddle.net/F8Q44/19/
Thanks to shoky in #jquery of freenode IRC for providing a simpler answer.
Alternatively, you can force a redraw to flush the batched style changes. This can be done, for example, by accessing the element's offsetHeight property:
$('img').show().each(function() {
this.offsetHeight;
}).prop('src', 'image src');
Demo: http://jsfiddle.net/F8Q44/266/
Just because I still need this every now and then I figured the pure JS function I use might be helpful for someone else. This is a pure JS way of restarting an animated gif, without reloading it. You can call this from a link and/or document load event.
<img id="img3" src="../_Images/animated.gif">
<a onClick="resetGif('img3')">reset gif3</a>
<script type="text/javascript">
// reset an animated gif to start at first image without reloading it from server.
// Note: if you have the same image on the page more than ones, they all reset.
function resetGif(id) {
var img = document.getElementById(id);
var imageUrl = img.src;
img.src = "";
img.src = imageUrl;
};
</script>
On some browsers you only need to reset the img.src to itself and it works fine. On IE you need to clear it before resetting it. This resetGif() picks the image name from the image id. This is handy in case you ever change the actual image link for a given id because you do not have to remember to change the resetGiF() calls.
--Nico
This solution preloads the gif and takes it out of the dom and then back in the src (thus avoiding another download)
I just tested it using jquery to remove the attribute and it works fine.
Example:
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js" type="text/javascript"></script>
<script>
$(function() {
$('.reset').click(resetGif);
function resetGif()
{
$('.img1').removeAttr('src', '');
}
});
</script>
</head>
<body>
<img class="img1" src="1.gif" />
reset gif
</body>
</html>
This seemed to work for me in Chrome, it runs each time just before I fade in the image and clears then refills the src and my animation now starts from the beginning every time.
var imgsrc = $('#my_image').attr('src');
$('#my_image').attr('src', '');
$('#my_image').attr('src', imgsrc);
I've a button with the an animated no-loop image in it. I just reload the image with some jquery and this seems to be working for me.
var asdf = $(".settings-button img").attr("src");
$(".settings-button img").attr("src", "").attr("src", asdf);
here's my hack for background images:
$(document).on('mouseenter', '.logo', function() {
window.logo = (window.logocount || 0) + 1;
var img = new Image();
var url = "/img/mylogimagename.gif?v=" + window.logocount;
var that = this;
$(img).load(function(){
$(that ).css('background-image','url(' + url + ')');
});
img.src = url;
});
I experienced problems with all of the above solutions. What finally worked was replacing the src temporarily with a transparent 1px gif:
var transparent1PxGif = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
var reloadGif = function(img) {
var src = img.src;
img.src = transparent1PxGif;
img.offsetHeight; // triggers browser redraw
img.src = src;
};
It's been several years and I've decided to revisit this since we have a number of new options at our disposal.
The issue with my previous answer is that it forces a re-download of the GIF every single time you want to re-start it. While that's fine for small files, it's still an overhead that's best avoided if possible.
With that in mind, I've got a new solution that uses AJAX to download the GIF once, and then converts it into a data URL (via a FileReader) and uses that as the source with a random query string attached.
This way, the browser only ever downloads the image once, and can even cache it properly, and the "reset" pulls from that pre-downloaded resource.
The only catch, of course, is that you have to make sure it's properly loaded before you can use it.
Demo: http://adamhaskell.net/misc/numbers/numbers.html
Relevant code:
var url = "something.gif"; // fallback until the FileReader is done
function setup() {
var xhr = new XMLHttpRequest();
xhr.open("GET",url,true);
xhr.responseType = "blob";
xhr.onreadystatechange = function() {
if( this.readyState == 4) {
var fr = new FileReader();
fr.onload = function() {
url = this.result; // overwrite URL with the data one
};
fr.readAsDataURL(this.response);
}
};
xhr.send();
}
function getGIF() {
return url+"?x="+Math.random();
}
Reset gif animation
When the browser render img, the source specified in src attribute is cached into memory for future reuse. This allows to increase the speed of page loading/reloading, as well as reduce the load on the network. And this behavior suits mostly everyone, because in reality, this is the most optimal and demanded option.
However, as always, there are exceptions. I came up with a dataUrl-based animation update option that solves several problems.
Issues solved:
Need to display gif images with animation without a loop (loop = 1), which may have the same src. But when one such picture appears, it is necessary that it play the animation without changing the animation of other pictures with the same src. The same picture should be loaded from server only once. Stackoverflow
Reset gif animation.
Start animation on hover
Reset src attribute
If we use a solution that clears the src attribute of an image, then all images with the same source will replay their animation. Unfortunately, I still did not fully understand why this is happening, but it interferes with correct work.
Cons
Reset animation of all images with the same src.
There are problems in mobile devices
Pros
Easy and fast
Modify url with random query
This solution consists in adding a random query parameter to the end of the src attribute, so that all images will have a different source, and therefore they will animate independently of each other. There is one big fat NO: this will lead to a constant request to the server to download the picture, and therefore they will no longer be cached. And if we need to display 100 identical pictures, then there will be 100 requests to the server. Rough and tough, but it always works.
Cons
Each picture with a unique query will be reloaded from the server.
Pros
Easy and fast
Modify dataUrl (Proposed Solution)
Data URLs, URLs prefixed with the data: scheme, allow content creators to embed small files inline in documents. They were formerly known as "data URIs" until that name was retired by the WHATWG.
MDN
The dataUrl structure from this documentation:
data:[<mediatype>][;base64],<data>
And this is how it is indicated in the specification:
dataurl := "data:" [ mediatype ] [ ";base64" ] "," data
mediatype := [ type "/" subtype ] *( ";" parameter )
data := *urlchar
parameter := attribute "=" value
If you look closely at the description of mediatype, then some strange parameter is indicated there. But, there is also a specification:
attribute := token
; Matching of attributes
; is ALWAYS case-insensitive.
value := token / quoted-string
token := 1*<any (US-ASCII) CHAR except SPACE, CTLs, or tspecials>
tspecials := "(" / ")" / "<" / ">" / "#" /
"," / ";" / ":" / "\" / <">
"/" / "[" / "]" / "?" / "="
; Must be in quoted-string,
; to use within parameter values
As can be seen, we can specify any parameter, the main thing is that it meets the requirements presented above!
Therefore, we can embed an additional attribute in the mediatype, which will not affect the image in any way, but the data url will differ from the same image.
Generalized algorithm:
We load the image through a regular request and remove the metadata from created dataUrl from blob.
fetch("https://cdn140.picsart.com/330970106009201.gif").then(async (res) => {
const blob = await res.blob();
const reader = new FileReader();
reader.onload = (ev) => {
// It would be reasonable to remove metadata to the point!
// But for simplicity, I'm using this implementation.
const dataUrl = ev.currentTarget.result.replace(
"data:image/gif;base64",
""
);
};
reader.readAsDataURL(blob);
});
Create/edit img element with src attribute "src=data:image/gif;base64;${Math.random()}${dataUrl}"
That is all!
Example Vanila JS
const url = "https://cdn140.picsart.com/330970106009201.gif";
function loadImage(src) {
fetch(src)
.then((res) => res.blob())
.then(async(blob) => {
const reader = new FileReader();
reader.onload = (ev) => {
const dataUrl = ev.currentTarget.result.replace("data:image/gif;base64", "")
const container = document.getElementById("container");
while (container.firstChild) {
container.firstChild.remove()
}
for (let i = 0; i < 6; i++) {
const img = document.createElement("img");
img.setAttribute("src", `data:image/gif;base64;gif-id=${Date.now()}${dataUrl}`)
container.appendChild(img);
img.addEventListener('click', ev => {
img.setAttribute("src", `data:image/gif;base64;gif-id=${Date.now()}${dataUrl}`)
})
}
};
reader.readAsDataURL(blob);
});
}
loadImage(url);
function updateImage() {
const newSrc = document.getElementById("image-src");
loadImage(document.getElementById("image-src").value);
}
#main {
display: flex;
flex-direction: column;
gap: 5px;
}
img {
width: 128px;
height: 128px;
padding: 5px;
}
<div id="main">
<label>Change gif url if current will become unavailable </label>
<input id="image-src" value="https://cdn140.picsart.com/330970106009201.gif"></input>
<button onclick="updateImage()">Update image source attribute</button>
<span>Click to reset!</span>
<div id="container">
</div>
</div>
Example React
import React, { useState, useRef } from "react";
function App() {
const [stars, setStars] = useState(0);
const [data, setData] = useState(null);
const ref = useRef(null);
React.useEffect(() => {
fetch("https://cdn140.picsart.com/330970106009201.gif")
.then((res) => res.blob())
.then(async (text) => {
const reader = new FileReader();
reader.onload = (ev) => {
setData(ev.currentTarget.result.replace("data:image/gif;base64", ""));
};
reader.readAsDataURL(text);
});
}, []);
return (
<React.Fragment>
<p onClick={() => setStars((s) => s + 1)}>+</p>
{data &&
new Array(stars).fill().map((s, ind) => {
return <Star src={data} key={ind}></Star>;
})}
<p onClick={() => setStars((s) => (s === 0 ? 0 : s - 1))}>-</p>
</React.Fragment>
);
}
export function Star(props) {
const [id] = useState(Math.random());
return (
<img
className="icon"
src={`data:image/gif;base64;gif-id=${id}` + props.src}
alt="animated star"
/>
);
}
export default App;
I came across this thread after searching many others. David Bell's post led me to the solution I needed.
I thought I'd post my experience in the event that it could be useful for anyone trying to accomplish what I was after. This is for an HTML5/JavaScript/jQuery web app that will be an iPhone app via PhoneGap. Testing in Chrome.
The Goal:
When user taps/clicks button A, an animated gif appears and plays.
When user taps/clicks button B, gif disappears.
When user taps/clicks button A again, after tapping/clicking button
B, animated gif should reappear and play from the beginning.
The Problem:
On tap/click of button A, I was appending the gif to an existing div. It would play fine.
Then, on tap/click of button B, I was hiding the container div, then setting the img src of the gif to an empty string (''). Again, no problem (that is, the problem wasn't evident yet.)
Then, on tap/click of button A, after tap/click of button B, I was re-adding the path to the gif as the src.
- This did not work. The gif would show up on subsequent taps/clicks of button A...however, the more I tapped/clicked button A, the more times the gif would load and start over. That is, if I went back and forth, tapping/clicking button A then button B 3 times, the gif would appear and then start/stop/start 3 times...and my whole app started to chug. I guess the gif was being loaded multiple times, even though I had set the src to an empty string when button B was tapped/clicked.
The Solution:
After looking at David Bell's post, I arrived at my answer.
I defined a global variable (let's call it myVar) that held the container div and the image (with the source path) within.
On the tap/click function of button A, I appended that container div to an existing parent div in the dom.
In that function, I created a new variable that holds the src path of the gif.
Just like David suggested, I did this (plus an append):
$('#mainParent').append(myVar);
var imgsrc = $('#my_image').attr('src');
$('#my_image').attr('src', '');
$('#my_image').attr('src', imgsrc);
THEN, in the function for button B, I set the src to an empty string and then removed the div containing the gif:
$('#my_image').attr('src', '');
$('#mainParent').find('#my_image').remove();
Now, I can tap/click button A then button B then button A, etc., all day long. The gif loads and plays on tap/click of button A, then hides on tap/click of button B, then loads and plays from the beginning on subsequent taps of button A every time with no issues.
I worked out a complete solution for this problem. It can be found here: https://stackoverflow.com/a/31093916/1520422
My solution restarts the animation WITHOUT re-loading the image data from the network.
It also enforces the image to repaint to fix some painting artefacts that occured (in chrome).

How can I determine if an image has loaded, using Javascript/jQuery?

I'm writing some Javascript to resize the large image to fit into the user's browser window. (I don't control the size of the source images unfortunately.)
So something like this would be in the HTML:
<img id="photo"
src="a_really_big_file.jpg"
alt="this is some alt text"
title="this is some title text" />
Is there a way for me to determine if the src image in an img tag has been downloaded?
I need this because I'm running into a problem if $(document).ready() is executed before the browser has loaded the image. $("#photo").width() and $("#photo").height() will return the size of the placeholder (the alt text). In my case this is something like 134 x 20.
Right now I'm just checking if the photo's height is less than 150, and assuming that if so it is just alt text. But this is quite a hack, and it would break if a photo is less than 150 pixels tall (not likely in my particular case), or if the alt text is more than 150 pixels tall (could possibly happen on a small browser window).
Edit: For anyone wanting to see the code:
$(function()
{
var REAL_WIDTH = $("#photo").width();
var REAL_HEIGHT = $("#photo").height();
$(window).resize(adjust_photo_size);
adjust_photo_size();
function adjust_photo_size()
{
if(REAL_HEIGHT < 150)
{
REAL_WIDTH = $("#photo").width();
REAL_HEIGHT = $("#photo").height();
if(REAL_HEIGHT < 150)
{
//image not loaded.. try again in a quarter-second
setTimeout(adjust_photo_size, 250);
return;
}
}
var new_width = . . . ;
var new_height = . . . ;
$("#photo").width(Math.round(new_width));
$("#photo").height(Math.round(new_height));
}
});
Update: Thanks for the suggestions. There is a risk of the event not being fired if I set a callback for the $("#photo").load event, so I have defined an onLoad event directly on the image tag. For the record, here is the code I ended up going with:
<img id="photo"
onload="photoLoaded();"
src="a_really_big_file.jpg"
alt="this is some alt text"
title="this is some title text" />
Then in Javascript:
//This must be outside $() because it may get called first
var isPhotoLoaded = false;
function photoLoaded()
{
isPhotoLoaded = true;
}
$(function()
{
//Hides scrollbars, so we can resize properly. Set with JS instead of
// CSS so that page doesn't break with JS disabled.
$("body").css("overflow", "hidden");
var REAL_WIDTH = -1;
var REAL_HEIGHT = -1;
$(window).resize(adjust_photo_size);
adjust_photo_size();
function adjust_photo_size()
{
if(!isPhotoLoaded)
{
//image not loaded.. try again in a quarter-second
setTimeout(adjust_photo_size, 250);
return;
}
else if(REAL_WIDTH < 0)
{
//first time in this function since photo loaded
REAL_WIDTH = $("#photo").width();
REAL_HEIGHT = $("#photo").height();
}
var new_width = . . . ;
var new_height = . . . ;
$("#photo").width(Math.round(new_width));
$("#photo").height(Math.round(new_height));
}
});
Either add an event listener, or have the image announce itself with onload. Then figure out the dimensions from there.
<img id="photo"
onload='loaded(this.id)'
src="a_really_big_file.jpg"
alt="this is some alt text"
title="this is some title text" />
Using the jquery data store you can define a 'loaded' state.
<img id="myimage" onload="$(this).data('loaded', 'loaded');" src="lolcats.jpg" />
Then elsewhere you can do:
if ($('#myimage').data('loaded')) {
// loaded, so do stuff
}
The right answer, is to use event.special.load
It is possible that the load event will not be triggered if the image is loaded from the browser cache. To account for this possibility, we can use a special load event that fires immediately if the image is ready. event.special.load is currently available as a plugin.
Per the docs on .load()
You want to do what Allain said, however be aware that sometimes the image loads before dom ready, which means your load handler won't fire. The best way is to do as Allain says, but set the src of the image with javascript after attaching the load hander. This way you can guarantee that it fires.
In terms of accessibility, will your site still work for people without javascript? You may want to give the img tag the correct src, attach you dom ready handler to run your js: clear the image src (give it a fixed with and height with css to prevent the page flickering), then set your img load handler, then reset the src to the correct file. This way you cover all bases :)
As per one of the recent comments to your original question
$(function() {
$(window).resize(adjust_photo_size);
adjust_photo_size();
function adjust_photo_size() {
if (!$("#photo").get(0).complete) {
$("#photo").load(function() {
adjust_photo_size();
});
} else {
...
}
});
Warning This answer could cause a serious loop in ie8 and lower, because img.complete is not always properly set by the browser. If you must support ie8, use a flag to remember the image is loaded.
Try something like:
$("#photo").load(function() {
alert("Hello from Image");
});
There's a jQuery plugin called "imagesLoaded" that provides a cross-browser compatible method to check if an element's image(s) have been loaded.
Site: https://github.com/desandro/imagesloaded/
Usage for a container that has many images inside:
$('container').imagesLoaded(function(){
console.log("I loaded!");
})
The plugin is great:
works for checking a container with many images inside
works for check an img to see if it has loaded
I found this worked for me
document.querySelector("img").addEventListener("load", function() { alert('onload!'); });
Credit goes totaly to Frank Schwieterman, who commented on accepted answer. I had to put this here, it's too valuable...
Any comments on this one?
...
doShow = function(){
if($('#img_id').attr('complete')){
alert('Image is loaded!');
} else {
window.setTimeout('doShow()',100);
}
};
$('#img_id').attr('src','image.jpg');
doShow();
...
Seems like works everywhere...
I just created a jQuery function to load an image using jQuerys Deferred Object which makes it very easy to react on load/error event:
$.fn.extend({
loadImg: function(url, timeout) {
// init deferred object
var defer = $.Deferred(),
$img = this,
img = $img.get(0),
timer = null;
// define load and error events BEFORE setting the src
// otherwise IE might fire the event before listening to it
$img.load(function(e) {
var that = this;
// defer this check in order to let IE catch the right image size
window.setTimeout(function() {
// make sure the width and height are > 0
((that.width > 0 && that.height > 0) ?
defer.resolveWith :
defer.rejectWith)($img);
}, 1);
}).error(function(e) {
defer.rejectWith($img);
});
// start loading the image
img.src = url;
// check if it's already in the cache
if (img.complete) {
defer.resolveWith($img);
} else if (0 !== timeout) {
// add a timeout, by default 15 seconds
timer = window.setTimeout(function() {
defer.rejectWith($img);
}, timeout || 15000);
}
// return the promise of the deferred object
return defer.promise().always(function() {
// stop the timeout timer
window.clearTimeout(timer);
timer = null;
// unbind the load and error event
this.off("load error");
});
}
});
Usage:
var image = $('<img />').loadImg('http://www.google.com/intl/en_com/images/srpr/logo3w.png')
.done(function() {
alert('image loaded');
$('body').append(this);
}).fail(function(){
alert('image failed');
});
See it working at: http://jsfiddle.net/roberkules/AdWZj/
This function checks if an image is loaded based on having measurable dimensions. This technique is useful if your script is executing after some of the images have already been loaded.
imageLoaded = function(node) {
var w = 'undefined' != typeof node.clientWidth ? node.clientWidth : node.offsetWidth;
var h = 'undefined' != typeof node.clientHeight ? node.clientHeight : node.offsetHeight;
return w+h > 0 ? true : false;
};
We developed a page where it loaded a number of images and then performed other functions only after the image was loaded. It was a busy site that generated a lot of traffic. It seems that the following simple script worked on practically all browsers:
$(elem).onload = function() {
doSomething();
}
BUT THIS IS A POTENTIAL ISSUE FOR IE9!
The ONLY browser we had reported issues on is IE9. Are we not surprised? It seems that the best way to solve the issue there is to not assign a src to the image until AFTER the onload function has been defined, like so:
$(elem).onload = function() {
doSomething();
}
$(elem).attr('src','theimage.png');
It seems that IE 9 will sometimes not throw the onload event for whatever reason. Other solutions on this page (such as the one from Evan Carroll, for example) still did not work. Logically, that checked if the load state was already successful and triggered the function and if it wasn't, then set the onload handler, but even when you do that we demonstrated in testing that the image could load between those two lines of js thereby appearing not loaded to the first line and then loading before the onload handler is set.
We found that the best way to get what you want is to not define the image's src until you have set the onload event trigger.
We only just recently stopped supporting IE8 so I can't speak for versions prior to IE9, otherwise, out of all the other browsers that were used on the site -- IE10 and 11 as well as Firefox, Chrome, Opera, Safari and whatever mobile browser people were using -- setting the src before assigning the onload handler was not even an issue.
May I suggest a pure CSS solution altogether?
Just have a Div that you want to show the image in. Set the image as background. Then have the property background-size: cover or background-size: contain depending on how you want it.
cover will crop the image until smaller sides cover the box.
contain will keep the entire image inside the div, leaving you with spaces on sides.
Check the snippet below.
div {
height: 300px;
width: 300px;
border: 3px dashed grey;
background-position: center;
background-repeat: no-repeat;
}
.cover-image {
background-size: cover;
}
.contain-image {
background-size: contain;
}
<div class="cover-image" style="background-image:url(https://assets1.ignimgs.com/2019/04/25/avengers-endgame-1280y-1556226255823_1280w.jpg)">
</div>
<br/>
<div class="contain-image" style="background-image:url(https://assets1.ignimgs.com/2019/04/25/avengers-endgame-1280y-1556226255823_1280w.jpg)">
</div>
I find that this simple solution works best for me:
function setEqualHeight(a, b) {
if (!$(a).height()) {
return window.setTimeout(function(){ setEqualHeight(a, b); }, 1000);
}
$(b).height($(a).height());
}
$(document).ready(function() {
setEqualHeight('#image', '#description');
$(window).resize(function(){setEqualHeight('#image', '#description')});
});
</script>
image.complete might be another option https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/complete

Categories

Resources