Gifs, Javascript and Multiple Instances - javascript

Maybe a silly question but here goes anyway.
Example: Let's say I have one non-looping animated GIF and I have two img elements.
<img src="" id="slot1" />
<img src="" id="slot2" />
So I use a little javascript to change the source of the slot1.
function changE(x)
{var image=document.getElementById (x);
image.src="animated.gif";
}
someButtonGotClicked=changE('slot1');
that works fine. Gif plays from start to end but if I then change the src of slot2 to the same gif:
changE('slot2');
slot1 resets it's gif back to the start to sync with slot2 starting it's gif.
Now i'm aware I could copy the gif and have 2 seperate files to use and I know about sprite sheets but I'm curious If I can use one copy of a gif and have it used multiple times on a page without all instances of the gif being restarted everytime another img element recieves the same file as it's src?
Hope that wasn't confusing. Thanks.

Once an image is loaded in memory, all other objects that request that image, using the same URL, get a reference to the image from the browser cache. This avoids loading the same image multiple times. In the case of a gif, one of the meta data to be kept track of is the current frame, which is stored not at the <img> dom element, but rather at the browser level in the structure it uses to store that gif.
On load, that frame index is reset. So, while the browser is processing the gif loop, a second image loading sets the current frame index to the beginning, hence both images synchronize.
This is a browser implementation, and it seems most browsers follow this implementation. One advantage to this is that if you have thousands of little gifs (from the same URL) in one page, the browser would do a lot less computation to render them, because it would only change one frame index, not thousands.
Edit:
To fix your code you'd have to basically add something random at the end of your image.
function changE(x)
{var image=document.getElementById (x);
image.src="animated.gif?" + Math.random();
}
So the browser thinks this is a different image (i.e. different URL).

Try using a solution like so:
<img src="" id="slot1" class="animate" />
<img src="" id="slot2" class="animate" />
(function(doc, w) {
var changE, getElementsByClassName;
changE = function(img) {
img.src = "animated.gif";
};
getElementsByClassName = function(node, classname) {
if (node.getElementsByClassName) { // use native implementation if available
return node.getElementsByClassName(classname);
} else {
return (function getElementsByClass(searchClass, node) {
if (node == null)
node = doc;
var classElements = [],
els = node.getElementsByTagName("*"),
elsLen = els.length,
pattern = new RegExp("(^|\\s)" + searchClass + "(\\s|$)"), i, j;
for (i = 0, j = 0; i < elsLen; i++) {
if (pattern.test(els[i].className)) {
classElements[j] = els[i];
j++;
}
}
return classElements;
})(classname, node);
}
};
w.onload = function() {
var imgs, i = 0, l;
imgs = getElementsByClassName(doc, 'animate');
l = imgs.length;
for (i; i < l; i++) {
imgs[i].onclick = function(e) { changE(this); };
}
};
})(document, window);
This will set a clicke event for each image with the calss name animate and the click event will only effect the specific image clicked.

Related

Preloading then adding additional images after page load to an image gallery on button click

I am building an image gallery that is populated from a JSON file. Everything works as intended currently, but as of right now there is no pre-loading of content after the initial page load. What I would like to happen is after the "view more" button is clicked I will have some "loading" text show, the batch of images will preload, the "loading" text will disappear, then the images will be added to the page once all items have loaded.
Here is the section of the code that involves the JSON fetch request and the building of elements on the page:
var HTML = '';
var itemsStart = 6; // Starting number of items on page.
var itemsAdd = 9; // Number of items to add to page at a time via button click.
var pItems = document.getElementById('pItems');
var pWrapper = document.getElementById('pItemWrapper');
function addProjects() {
pItems.insertAdjacentHTML('beforeend', HTML);
console.log('BUILD PROJECTS');
}
//Load json
fetch('data/projects.json').then(function (response) {
return response.json();
}).then(function (data){
//Loop through first set of items to load on page.
for (var i = 0; i < itemsStart; i++) {
HTML += '<img src="' + data.projects[i].Image + '" alt=""></img>';
if (i == (itemsStart - 1)) {
addProjects();
}
}
//Load additional items when clicking 'view more'.
document.getElementById('view-more').addEventListener('click', function() {
HTML = '';
for (var i = itemsStart; i < itemsStart + itemsAdd; i++) {
if ((i < data.projects.length)) {
HTML += '<img src="' + data.projects[i].Image + '" alt=""></img>';
}
if (i == ((itemsStart + itemsAdd) - 1) ) {
addProjects();
}
}
itemsStart = itemsStart + itemsAdd;
});
}).catch(function(error) {
console.error('Something went wrong');
});
I'm not using jQuery so I'd like to stick to vanilla js. I don't know what I need to add to my button event listener beyond what I have, I've never tried preloading images like this without using a plugin but I feel like I don't need to load an entire plugin just for this one thing and I'd like to understand how this would work.
EDIT
I feel like I'm almost there, but I still have something wrong. I made some modifications to have each item inside its own container, but instead of that happening I am creating an empty container for each pass of the loop, then the last container gets each image added to it. My code looks like this:
var itemsAdd = 3;
//Load additional items when clicking 'view more'.
document.getElementById('view-more').addEventListener('click', function() {
//The loop will add the next 3 items in the json file per click.
for (var i = itemsStart; i < itemsStart + itemsAdd; i++) {
var placeholder = document.createElement('div');
var src = 'img/portfolio/' + data.projects[i].url;
placeholder.innerHTML= '<div class="img-container">' + data.projects[i].Title + '</div>';
var galleryItem = placeholder.firstChild;
preloadImage(src).then(function (image) {
galleryItem.append(image);
});
pItems.append(galleryItem);
}
itemsStart = itemsStart + itemsAdd;
});
The result I get is this:
Is this because of how the promise works for the preloadImage function?
Generally you would create an image with JavaScript through either document.createElement('img') or the Image() constructor. Both result an in instance of an HTMLImageElement.
With this, you'll have an image that is not connected to the DOM, so it's not visible to you or the user. You can use this element to load the image behind the scenes by setting the image' src property.
Then by listening to the onload event you can determine whenever the image has finished loading. From here you could continue your flow by adding the image to the DOM and, for example, fade it in with animation.
The example below shows this process in the form of a function that returns a Promise. This promise will resolve whenever the load event has been triggered.
const preloadImage = src =>
new Promise(resolve => {
const image = new Image();
const onLoad = () => {
resolve(image);
};
image.addEventListener('load', onLoad, {once: true});
image.src = src;
});
Using it should be like this:
const src = 'http://example.com/my-image.jpg';
preloadImage(src).then(image => {
// Here the image has been loaded and is available to be appended, or otherwise.
document.body.append(image);
});
In your case you would loop over each image, call the function while passing the URL of the image, and append the image to the DOM when it's finished loading.
You can handle any animations, like fade-ins with CSS.
Real world implementation
So how should you implement this in your project? You'll need to start at the point where you create your images. Currently your images are created as strings. But strings are just strings, they aren't HTML elements, yet.
I'd recommend that you'll create a placeholder for each image. This placeholder could visually indicate that an image is loading and act as a wrapper for the image. Add this placeholder immediately to the pItems element.
Then load the image for each Image in your data.projects array by calling the preloadImage. Whenever the image is loaded, append it to the placeholder we've just created. You should now have the effect that first a placeholder is added and the images are starting to appear one by one.
The same logic should be applied for the load more loop.
...
}).then(function (data){
for (let i = 0; i < itemsStart; i++) {
// Create a <div class="placeholder"> which should act as a placeholder / wrapper.
const placeholder = document.createElement('div');
placeholder.classList.add('placeholder');
// Create the image based on the Image value.
// Whenever the image is loaded, add it to the placeholder.
const src = data.projects[i].Image;
preloadImage(src).then(image => {
placeholder.append(image);
});
// Immediately add the placeholder.
// This line doesn't wait for preloadImage to finish because preloadImage is asynchronous. Look into Promises if that is new to you.
pItems.append(placeholder);
}
...
});
From here you've got control over how the placeholder should look and any animations an image inside that placeholder should have.
I think you could put a <div> with black background over the loading image using css and when the image is ready remove it with js. You can detect when the image is loaded using the img.onload = () => {} function.
Or you could place there an img with the loading screen and replace it with the actual image when the image has loaded.

Javascript: how to execute a function within a function without delay?

I'm trying to make an automated image gallery using javascript to paste img-statements (+ some extras for css) into html.
All the images are in the same folder and are named image (1), image (2), ... Apparently client side can't know the name of the files in the folder nor the amount of files.
The append-process works fine, but I can't find a way to stop without giving the amount of pictures myself.
I'm trying to stop the append-process using ".onerror" when the browser can't find the next image, but it is always skipped until the end (resulting in an infinite loop (I'm using a "i<=6" for testing)).
Thanks! Here is my code:
function showgallery() {
let flag = true;
let i = 1;
while (i <= 6 && flag) {
$("#fotos").append(`<div class="cssbox">
<a id="image${i}" href="#image${i}"><img id="picture${i}" class="cssbox_thumb" src="../photogallery/image (${i}).jpg">
<span class="cssbox_full"><img src="../photogallery/image (${i}).jpg" /></span>
</a>
<a class="cssbox_close" href="#void"></a>
<a class="cssbox_prev" href="#image${i-1}"><</a>
<a class="cssbox_next" href="#image${i+1}">></a>
</div>`);
document.getElementById(`picture${i}`).onerror = () => {
flag = false;
}
i++;
}
}
window.onload = showgallery;
The reason why it's skipping some is because it takes time to load each image and for onerror to be called, yet you are doing this all in a loop which executes all the code without waiting for the error, so even if there is an error the flag variable won't be changed until the error event is fired, which happens after the current loop cycle
The way to do this instead is use recursion, if a certain condition is not met yet. Also I would recommend using Image for testing then appending it to the document
Also you don't even need error you can just only append it if it loads successfully
So you can use recursion here like
var cur = 0
function getImg() {
var img= new Image ()
img.src = "image (" + (cur++) + ").jpg"
img.onload = () => {
fotos.appendChild(img)//or other span elements etc that have img as a child
getImg()
}
//if you want to know when it ended then you can use .onerror
img.onerror = () => console.log("done")
}
getImg()

Finding the image count in a web page

I have a JS code, that gets called in different web pages of my clients. I want to fetch the total number of images. I want only those images that are visible to the user and not just any other images. This is my JS code
function getImageCount(topWindow) {
try {
var images = topWindow.document.getElementsByTagName('img');
var imageCount;
for (var i=0, length = images.length; i < length; i++) {
var image = images[i];
var clientWidth = image.clientWidth;
if(clientWidth && clientWidth > 1) {
var src = image.getAttribute('src');
if(src) {
src = src.toLowerCase();
if(src.indexOf('.jpg') !== -1 ||
src.indexOf('.jpeg') !== -1 ||
src.indexOf('.gif') !== -1 ||
src.indexOf('png') !== -1) {
imageCount = imageCount ? ++imageCount : 1;
}
}
}
}
return imageCount;
} catch (e) {
processError("getImageCount", e);
}
}
var imageCount = getImageCount(top);
I have been trying a lot to stabilize this code so that it works correctly across all different types of web pages. Basically what I want is a generic code that captures image counts correctly.
Eg:
My code gives image count as 1 for http://www.msn.com/en-us/news/other/one-free-agent-every-nfl-team-should-sign-this-offseason/ss-AAmLlC0#image
What I want is a GENERIC CODE that gives me a correct image count irrespective of where it runs. Can some one give me some detailed solutions.
I would appreciate a lot.
To simplt count all the images (<img>) on the page:
document.images.length
And to count all the "visible" images (ones with width and height):
[...document.images].filter(img => img.clientWidth && img.clientHeight).length
This will give you the number of images on the page. This does not include CSS images. since your code didn't either then I take it you want <img> ones
I didn't quite understand the meaning of irrespective of where it runs.. can you elaborate?
// Extract images
websiteImages = document.getElementsByTagName('img');
for (url in websiteImages)
console.log(websiteImages.length);
//Extract inbound and outbound links
Links = document.querySelectorAll('a');
for (link in Links)
console.log(Links[link].href);
Paste this Scripts into your console of browser
Check on the Below Link/ any Link you like
(https://news.google.com/topstories?hl=en-IN&gl=IN&ceid=IN:en&gl=IN&ceid=IN:en)
The above-mentioned script will give all the Images present in the webpages.
And the second script will give all the number of Inbound and Outbound/exit links
Just apply Some filter as per your use case and you will be good to go.
Use this simple approach for links
$$('a').length
To count the number of images on a webpage use the below code:
$$('img').length

How to know if an Image is given an illegal source?

I have created a list of Image object. Every Image's onload function is the same:
function tracker() {
++loadedImage;
if (loadedImage === total) ..
}
images[i].src = urls[i];
This function tries to see if all the images are loaded, if so, call another function. However, several sources are illegal (maybe not existing) so the onload function will never be called for those images. I want to increment loadedImage even if the object is given an illegal source. However can I know if an Image is given an illegal source?
Thank you.
Images also have an error event. You can hook it too and count failed loads from here.
If you plan to support IE8-, be aware that load events won't fire if the image happens to be already present in the browser's cache.
Depending on what you want to achieve (and when), window.onload might be an alternative. It fires, when the document and all resources are loaded
<img src="http://lorempixel.com/100/100"/>
<img src="http://lorempixel.com/a/b"/>
<img src="http://placehold.it/200x50"/>
window.onload = function(evt) {
console.log('window.onload');
}
var imgs = document.getElementsByTagName('img');
for (var i = 0; i < imgs.length; ++i) {
var img = imgs[i];
img.onload = function(evt) {
console.log('src=' + this.src + ', ready');
}
img.onerror = function(evt) {
console.log('src=' + this.src + ', error');
}
}
JSFiddle

JavaScript Timed Image Swap Need Help

So in my script I have...
<script type="text/javascript">
var images = new Array();
var numImages = 3;
var index = 0;
function setupSwapper() {
for (i = 0; i < numImages; i++) {
images[i] = new Image(266, 217);
images[i].src = "images/image" + i + ".png";
}
setTimeout("swapImage()", 5000);
}
function swapImage() {
if (index >= numImages) {
index = 0;
}
document.getElementById('myImage').src = images[index].src
index++;
setTimeout("swapImage()", 5000);
}
</script>
And then I have <body onload="setupSwapper()"> to setup the body.
and <img width=266 height=217 id="myImage" name="myImage" src="images/image0.png"></img> elsewhere in my document.
Only the initial image (image0.png) is showing up. I'm probably blind from having looked at this so long. The images are not swapping.
Use FireBug or a similar tool for debugging what's going on:
Does the img DOM element in fact gets its src changed ?
Do you see any network activity trying to load the images ? does it succeed ?
Set up breakpoints in your code and see what happens in the debugger
BTW - You can use setInterval instead of setTimeout - it sets a repeating timer
You're missing the () in the definition of "setupSwapper".
Also it's setTimeout, not setTimeOut.
Finally, get rid of the "type" attribute on your <script> tag.
You might want to start "index" at 1 instead of 0.
The way to go:
setTimeout(swapImage, 5000);
[FORGET] the type attribute has nothing to do with it
[FORGET] the index has nothing to do with it
[OPTIONAL] remove "name" attribute from the image (useless)
[OPTIONAL] close image tags like <img />
Note: 2-5 is about correctness. Only the first one is important to get it work.
Get Firebug, use it's debugger to put breakpoints inside swapImage to see if it is hit after the timeout. Another way is to use the console.* apis to see what's happening(e.g. console.log).

Categories