I am cofused who I should use callbacks in this situation:
I and fire displayHighlights() function after highlightImages() is finished, so I don't need to use setTimeout()
First function is validation if images are not throwing errors, second is displaying only three of them.
<ul class="clear">
<li style="display : none" class="highlight-photos"><a class="highlight_photo"></a></li>
<li style="display : none" class="highlight-photos"><a class="highlight_photo"></a></li>
<li style="display : none" class="highlight-photos"><a class="highlight_photo"></a></li>
</ul>
function highlightImages() {
$(".highlight_photo").each(function() {
var fileName = $(this).data('url')
var image = new Image();
var that = $(this);
image.onerror = badImage;
image.src = fileName;
function badImage( event) {
var el = $(".highlight_photo[data-url='" + fileName+ "']");
el.parent().remove();
}
});
setTimeout(function(){displayHighlights();},500);
};
function displayHighlights() {
if ($(".highlight_photo").parent().length <= 3 ) {
$(".highlight-photos").show();
} else {
$("ul.clear li:lt(3)").addClass("visible");
$(".highlight-photos").not(".visible").remove();
$("ul.clear li:lt(3)").show();
}
}
One solution would be to add an onlaod event to all of the images.
So when adding the image increase a counter like : images loading.
Before!!! adding src attribute to the image attach an onload handler, that will decrease the same counter.
var images_loading = 0; //setting the counter
function highlightImages() {
$(".highlight_photo").each(function() {
var fileName = $(this).data('url')
var image = new Image();
var that = $(this);
image.onerror = badImage;
images_loading++; //yuou are loading one more image
image.onload = function(){images_loading--; //when it is loaded, it is ok}
image.src = fileName;
function badImage( event) {
images_loading--; //if it fails, remove from loading
var el = $(".highlight_photo[data-url='" + fileName+ "']");
el.parent().remove();
}
});
var can_highlight = false;
while(!can_highlight){
if(images_loading == 0){
//all the images are loaded
can_highlight = true;
displayHighlights();
} else {
setTimeout(1) //sparing cpu
}
}
};
I would try using a promise for your each statement.
For example:
JavaScript:
$.when(highlightImages()).done(function () {
console.log('highlightImages complete!');
displayHighlights();
});
function highlightImages() {
$(".highlight_photo").each(function (key, val) {
console.log(val);
});
};
function displayHighlights() {
console.log('displayHighlights called');
};
JSFiddle
You can also use jquery deferred function as described here
best
M
Related
Here is a contenteditable="true" DIV. When we paste any image in this div then it appends into document.body.appendChild with blob URL.
And then the HTML structure become like this:
<img src="blob:https://www.some.com/0606f154-ce49-4048-9321-27778280b2d3">
<img src="blob:https://www.some.com/0606f154-ce49-4048-9321-27778280b2d3">
But I want this image appended, document.body.appendChild(image);, into a div class when it gets appended.
Like this:
<div class="someclass"><img src="blob:https://www.some.com/0606f154-ce49-4048-9321-27778280b2d3"></div>
<div class="someclass"><img src="blob:https://www.some.com/0606f154-ce49-4048-9321-27778280b2d3"></div>
I tried these many things, but it didn't work:
document.body.appendChild(`<div class="someclass">` + image + '</div> );
My Whole Code is:
var PasteImage = function (el) {
this._el = el;
this._listenForPaste();
};
PasteImage.prototype._getImageFromContentEditableOnNextTick = function () {
var self = this;
// We need to wait until the next tick as Firefox will not have added the image to our
// contenteditable element
setTimeout(function () {
self._getImageFromContentEditable();
});
};
PasteImage.prototype._getURLObj = function () {
return window.URL || window.webkitURL;
};
PasteImage.prototype._pasteImage = function (image) {
this.emit('paste-image', image);
};
PasteImage.prototype._pasteImageSource = function (src) {
var self = this,
image = new Image();
image.onload = function () {
self._pasteImage(image);
};
image.src = src;
};
PasteImage.prototype._onPaste = function (e) {
// We need to check if event.clipboardData is supported (Chrome & IE)
if (e.clipboardData && e.clipboardData.items) {
// Get the items from the clipboard
var items = e.clipboardData.items;
// Loop through all items, looking for any kind of image
for (var i = 0; i < items.length; i++) {
if (items[i].type.indexOf('image') !== -1) {
// We need to represent the image as a file
var blob = items[i].getAsFile();
// Use a URL or webkitURL (whichever is available to the browser) to create a
// temporary URL to the object
var URLObj = this._getURLObj();
var source = URLObj.createObjectURL(blob);
// The URL can then be used as the source of an image
this._pasteImageSource(source);
// Prevent the image (or URL) from being pasted into the contenteditable element
e.preventDefault();
}
}
}
else {
// If we can't handle clipboard data directly (Firefox & Safari), we need to read what was
// pasted from the contenteditable element
this._getImageFromContentEditableOnNextTick();
}
};
PasteImage.prototype._listenForPaste = function () {
var self = this;
self._origOnPaste = self._el.onpaste;
self._el.addEventListener('paste', function (e) {
self._onPaste(e);
// Preserve an existing onpaste event handler
if (self._origOnPaste) {
self._origOnPaste.apply(this, arguments);
}
});
};
// TODO: use EventEmitter instead
PasteImage.prototype.on = function (event, callback) {
this._callback = callback;
};
// TODO: use EventEmitter instead
PasteImage.prototype.emit = function (event, arg) {
this._callback(arg);
};
PasteImage.prototype._loadImage = function (src) {
return new Promise(function (resolve) {
var img = new Image();
img.onload = function () {
resolve(img);
};
img.src = src;
});
};
PasteImage.prototype._findFirstImage = function () {
var self = this;
return new Promise(function (resolve) {
for (var i in self._el.childNodes) {
var node = self._el.childNodes[i];
// Is the element an image?
if (node.tagName === 'IMG') {
resolve(node);
} else if (node.childNodes[0]) { // Are there children?
// If you copy an image from within Safari and then paste it within Safari, the image can be
// nested somewhere under the contenteditable element.
var imgs = node.getElementsByTagName('img');
if (imgs) {
resolve(imgs[0]);
}
}
}
// No image found so just resolve
resolve();
});
};
PasteImage.prototype._removeFirstImage = function () {
var self = this;
return self._findFirstImage().then(function (img) {
if (img) {
// In Safari if we copy and image and then paste an image within Safari we need to construct a
// proper image from the blob as Safari doesn't do this for us. Moreover, we need to wait for
// our converted image to be loaded before removing the image from the DOM as otherwise there
// can be a race condition where we remove the image before it has been loaded and this
// apparently stops the loading process.
return self._loadImage(img.src).then(function (loadedImage) {
img.parentElement.removeChild(img);
return loadedImage;
});
}
});
};
PasteImage.prototype._getImageFromContentEditable = function () {
var self = this;
this._removeFirstImage().then(function (img) {
// Process the pasted image
self._pasteImage(img);
});
};
// -----
var pasteImage = new PasteImage(document.getElementById('my-div'));
pasteImage.on('paste-image', function (image) {
document.body.appendChild( image );
});
<title>Paste Image Example</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.4.6/bluebird.min.js"></script>
<h1>
Copy an image and then press Command+V (Mac) or Ctrl+V (Windows) anywhere in the div below.
</h1>
<div id="my-div" contenteditable="true"
style="border:1px solid #777777;height: 50px;padding: 5px;"
onpaste="console.log('onpastefromhtml')">
</div>
How can I do that?
To make it clear, here is what you will need to do:
Just replace this:
var pasteImage = new PasteImage(document.getElementById('my-div'));
pasteImage.on('paste-image', function (image) {
document.body.appendChild( image );
});
With this:
var pasteImage = new PasteImage(document.getElementById('my-div'));
pasteImage.on('paste-image', function (image) {
// Create a new div element to put your image in:
var newDivElement = document.createElement("div");
// Add the image element to the div:
newDivElement.append(image);
// Append the div
document.body.append(newDivElement);
});
I think my javascript in my php file is in conflict with my javascript file.
I have a script that checks of the image is smaller then 2MB and a script that shows the image you selected in a small version. But the second part does not work when the first script is active. how do I fix this?
script in HTML
<script>
window.onload = function() {
var uploadField = document.getElementById("frontImages");
uploadField.onchange = function() {
if(this.files[0].size > 2000000){
alert("File is too big!");
this.value = "";
};
};
var uploadField = document.getElementById("itemImages");
uploadField.onchange = function() {
if(this.files[0].size > 200){
alert("File is too big!");
this.value = "";
};
};
}
</script>
.js file
$("#frontImages").change(function () {
if ($('#frontImages').get(0).files.length > 0) {
$('#frontImages').css('background-color', '#5cb85c');
} else {
$('#frontImages').css('background-color', '#d9534f');
}
});
$("#itemImages").change(function () {
if ($('#itemImages').get(0).files.length > 0) {
$('#itemImages').css('background-color', '#5cb85c');
} else {
$('#itemImages').css('background-color', '#d9534f');
}
});
document.getElementById("frontImages").onchange = function () {
var x = document.getElementById('previewFrontImage');
x.style.display = 'block';
var reader = new FileReader();
reader.onload = function (e) {
document.getElementById("previewFrontImage").src = e.target.result;
};
reader.readAsDataURL(this.files[0]);
};
function previewImages() {
var $preview = $('#previewItemImages').empty();
if (this.files) $.each(this.files, readAndPreview);
function readAndPreview(i, file) {
var reader = new FileReader();
$(reader).on("load", function () {
$preview.append($("<img/>", {src: this.result, height: 100}));
});
reader.readAsDataURL(file);
}
}
$('#itemImages').on("change", previewImages);
I'm guessing that the conflict is between the html script and this
document.getElementById("frontImages").onchange = function ()
I also have a question how I can fix that there will be no small image when the image is too big
Your guess is correct, onchange is simply member variable of various elements, and thus
var uploadField = document.getElementById("frontImages");
uploadField.onchange = function() {
and
document.getElementById("frontImages").onchange = function ()
are setting this single variable (of frontImages), which will store one callback function at a time.
You could use addEventListener() instead, which maintains a list of event listeners, so there can be more than one. Modifying the lines to
var uploadField = document.getElementById("frontImages");
uploadField.addEventListener("change", function() {
and
document.getElementById("frontImages").addEventListener("change", function ()
will register both event listeners on frontImages, regardless of the order they are executed.
Side remark: when you have "nice" ids, document.getElementById() can be omitted, as elements with ids become variables (of window which is the global scope), and thus you could write frontImages.addEventListener(...). You still need the getter in various cases, like when a local variable shadows the id, or when it is not usable as variable identifier (like id="my-favourite-id" or id="Hello World")
I am trying to create a function that will recursively try to reload an image until it either is successful, or a maximum amount of attempts is reached. I have created this function, but it doesn't work (is it due to the fact that the reference to the image has changed?):
function reload (image, URL, maxAttempts)
{
image.onerror = image.onabort = null;
if (maxAttempts > 0)
{
var newImg = new Image ();
newImg.src = URL;
newImg.onerror = image.onabort = reload (image, URL, maxAttempts - 1);
newImg.onload = function () {
newImg.onerror = newImg.onabort = null;
image = newImg;
}
}
else
{
alert ("Error loading image " + URL); /* DEBUG */
}
}
Which is used in the following manner:
var globalTestImage = new Image ();
reload (globalTestImage, "testIMG.jpg", 4);
Rather than it attempting to load "testIMG.jpg" four times, and waiting in between attempts, it instead tries to load it twice, and regardless of whether it was successful the second time around it will display the error message.
What am I doing there? More precisely, why is it acting the way it is, rather than retrying to load the image 4 times?
(function ($) {
var retries = 5; //<--retries
$( document).ready(function(){
$('img').one('error', function() {
var $image = $(this);
$image.attr('alt', 'Still didn\'t load');
if (typeof $image !== 'undefined') {
if (typeof $image.attr('src') !== 'undefined') {
$image.attr('src', retryToLoadImage($image));
}
}
});
});
function retryToLoadImage($img) {
var $newImg = $('<img>');
var $src = ($img.attr('src')) || '';
$newImg.attr('src', $src);
$newImg.one('error', function() {
window.setTimeout(function(){
if (retries > 0) {
retries--;
retryToLoadImage($newImg);
}
}, 1000); //<-retry interval
});
$newImg.one('load', function() {
return $newImg.attr('src');
});
}
})(jQuery);
Some code I wrote for the same case a while ago. Hope it helps you!
In the end I solve this issue in a simple (if inelegant) way:
try
{
canvas.getContext("2d").drawImage (testImage, 0, 0);
backgroundLoaded = true;
}
catch (err)
{
testImage = new Image ();
testImage.src = "placeholder.jpg";
}
The idea is that if an image failed to load, it will fail when rendering it on the canvas, producing an error. When such an error happens, we can create a new image and try again.
I have here a little script that I found and am using it to create a simple game of sorts...
/*
Here add:
'image_path': ['id_elm1', 'id_elm2']
"id_elm1" is the ID of the tag where the image is initially displayed
"id_elm2" is the ID of the second tag, where the image is moved, when click on the first tag
*/
var obimids = {
'http://www.notreble.com/buzz/wp-content/uploads/2011/12/les-claypool-200x200.jpg': ['lesto', 'les'],
'http://rs902.pbsrc.com/albums/ac223/walkingdeadheartbreaker/Muzak/Guitarists/LarryLalondePrimus.jpg~c200': ['lerto', 'ler'],
'http://www.noise11.com/wp/wp-content/uploads/2014/07/Primus-Alexander-200x200.jpg': ['timto', 'tim']
};
// function executed when click to move the image into the other tag
function whenAddImg() {
/* Here you can add a code to be executed when the images is added in the other tag */
return true;
}
/* From here no need to edit */
// create object that will contain functions to alternate image from a tag to another
var obaImg = new Object();
// http://coursesweb.net/javascript/
// put the image in element with ID from "ide"
obaImg.putImg = function(img, ide, stl) {
if(document.getElementById(ide)) {
document.getElementById(ide).innerHTML = '<img src="'+ img+ '" '+stl+' />';
}
}
// empty the element with ID from "elmid", add image in the other element associated to "img"
obaImg.alternateImg = function(elmid) {
var img = obaImg.storeim[elmid];
var addimg = (elmid == obimids[img][0]) ? obimids[img][1] : obimids[img][0];
$('#'+elmid+ ' img').hide(800, function(){
$('#'+elmid).html('');
obaImg.putImg(img, addimg, 'style="display:none;"');
$('#'+addimg+ ' img').fadeIn(500);
});
// function executed after the image is moved into "addimg"
whenAddImg();
}
obaImg.storeim = {}; // store /associate id_elm: image
// add 'image': 'id_elm1', and 'image': 'id_elm1' in "storeim"
// add the image in the first tag associated to image
// register 'onclick' to each element associated with images in "obimids"
obaImg.regOnclick = function() {
for(var im in obimids) {
obaImg.storeim[obimids[im][0]] = im;
obaImg.storeim[obimids[im][2]] = im;
obaImg.putImg(im, obimids[im][0], '');
document.getElementById(obimids[im][0]).onclick = function(){ obaImg.alternateImg(this.id); };
document.getElementById(obimids[im][3]).onclick = function(){ obaImg.alternateImg(this.id); };
}
}
obaImg.regOnclick(); // to execute regOnclick()
FIDDLE
When clicking the items it adds them to a container where I'd like them to be stored if the user navigates to another page. I have seen some local storage cookie code on another script
FIDDLE
var $chks = $('.compare').change(function () {
console.log('c', this)
if ($(this).is(':checked')) {
var img = $('<img>'),
findimg = $(this).closest('.box').find('img'),
data_term = findimg.data('term');
img.attr('src', findimg.attr('src'));
img.attr('data-term', data_term);
var input = '<input type="hidden" name="imagecompare" value="' + data_term + '">';
$('#area').find('div:empty:first').append(img).append(input);
} else {
var term = $(this).data('term'),
findboximage = $('#area > div > img[data-term=' + term + ']')
findboximage.parent('div').empty();
}
localStorage.setItem("imagecookie", $chks.filter(':checked').map(function () {
return $(this).data('term')
}).get().join(','));
});
$(document).on('click', '#area > div', function () {
$(this).empty();
localStorage.clear();
});
var cookie = localStorage.getItem("imagecookie");
if (cookie) {
var terms = cookie.split(',');
if (terms.length) {
$chks.filter($.map(terms, function (val) {
return '[data-term="' + val + '"]'
}).join()).prop('checked', true).change();
}
}
but can't figure how to apply something similar to this one. I would be grateful for any help or to be pointed to some useful places for help.
I want to know when an image has finished loading. Is there a way to do it with a callback?
If not, is there a way to do it at all?
.complete + callback
This is a standards compliant method without extra dependencies, and waits no longer than necessary:
var img = document.querySelector('img')
function loaded() {
alert('loaded')
}
if (img.complete) {
loaded()
} else {
img.addEventListener('load', loaded)
img.addEventListener('error', function() {
alert('error')
})
}
Source: http://www.html5rocks.com/en/tutorials/es6/promises/
Image.onload() will often work.
To use it, you'll need to be sure to bind the event handler before you set the src attribute.
Related Links:
Mozilla on Image.onload()
Example Usage:
window.onload = function () {
var logo = document.getElementById('sologo');
logo.onload = function () {
alert ("The image has loaded!");
};
setTimeout(function(){
logo.src = 'https://edmullen.net/test/rc.jpg';
}, 5000);
};
<html>
<head>
<title>Image onload()</title>
</head>
<body>
<img src="#" alt="This image is going to load" id="sologo"/>
<script type="text/javascript">
</script>
</body>
</html>
You can use the .complete property of the Javascript image class.
I have an application where I store a number of Image objects in an array, that will be dynamically added to the screen, and as they're loading I write updates to another div on the page. Here's a code snippet:
var gAllImages = [];
function makeThumbDivs(thumbnailsBegin, thumbnailsEnd)
{
gAllImages = [];
for (var i = thumbnailsBegin; i < thumbnailsEnd; i++)
{
var theImage = new Image();
theImage.src = "thumbs/" + getFilename(globals.gAllPageGUIDs[i]);
gAllImages.push(theImage);
setTimeout('checkForAllImagesLoaded()', 5);
window.status="Creating thumbnail "+(i+1)+" of " + thumbnailsEnd;
// make a new div containing that image
makeASingleThumbDiv(globals.gAllPageGUIDs[i]);
}
}
function checkForAllImagesLoaded()
{
for (var i = 0; i < gAllImages.length; i++) {
if (!gAllImages[i].complete) {
var percentage = i * 100.0 / (gAllImages.length);
percentage = percentage.toFixed(0).toString() + ' %';
userMessagesController.setMessage("loading... " + percentage);
setTimeout('checkForAllImagesLoaded()', 20);
return;
}
}
userMessagesController.setMessage(globals.defaultTitle);
}
Life is too short for jquery.
function waitForImageToLoad(imageElement){
return new Promise(resolve=>{imageElement.onload = resolve})
}
var myImage = document.getElementById('myImage');
var newImageSrc = "https://pmchollywoodlife.files.wordpress.com/2011/12/justin-bieber-bio-photo1.jpg?w=620"
myImage.src = newImageSrc;
waitForImageToLoad(myImage).then(()=>{
// Image have loaded.
console.log('Loaded lol')
});
<img id="myImage" src="">
You could use the load()-event in jQuery but it won't always fire if the image is loaded from the browser cache. This plugin https://github.com/peol/jquery.imgloaded/raw/master/ahpi.imgload.js can be used to remedy that problem.
If the goal is to style the img after browser has rendered image, you should:
const img = new Image();
img.src = 'path/to/img.jpg';
img.decode().then(() => {
/* set styles */
/* add img to DOM */
});
because the browser first loads the compressed version of image, then decodes it, finally paints it. since there is no event for paint you should run your logic after browser has decoded the img tag.
Here is jQuery equivalent:
var $img = $('img');
if ($img.length > 0 && !$img.get(0).complete) {
$img.on('load', triggerAction);
}
function triggerAction() {
alert('img has been loaded');
}
Not suitable for 2008 when the question was asked, but these days this works well for me:
async function newImageSrc(src) {
// Get a reference to the image in whatever way suits.
let image = document.getElementById('image-id');
// Update the source.
img.src = src;
// Wait for it to load.
await new Promise((resolve) => { image.onload = resolve; });
// Done!
console.log('image loaded! do something...');
}
these functions will solve the problem, you need to implement the DrawThumbnails function and have a global variable to store the images. I love to get this to work with a class object that has the ThumbnailImageArray as a member variable, but am struggling!
called as in addThumbnailImages(10);
var ThumbnailImageArray = [];
function addThumbnailImages(MaxNumberOfImages)
{
var imgs = [];
for (var i=1; i<MaxNumberOfImages; i++)
{
imgs.push(i+".jpeg");
}
preloadimages(imgs).done(function (images){
var c=0;
for(var i=0; i<images.length; i++)
{
if(images[i].width >0)
{
if(c != i)
images[c] = images[i];
c++;
}
}
images.length = c;
DrawThumbnails();
});
}
function preloadimages(arr)
{
var loadedimages=0
var postaction=function(){}
var arr=(typeof arr!="object")? [arr] : arr
function imageloadpost()
{
loadedimages++;
if (loadedimages==arr.length)
{
postaction(ThumbnailImageArray); //call postaction and pass in newimages array as parameter
}
};
for (var i=0; i<arr.length; i++)
{
ThumbnailImageArray[i]=new Image();
ThumbnailImageArray[i].src=arr[i];
ThumbnailImageArray[i].onload=function(){ imageloadpost();};
ThumbnailImageArray[i].onerror=function(){ imageloadpost();};
}
//return blank object with done() method
//remember user defined callback functions to be called when images load
return { done:function(f){ postaction=f || postaction } };
}
This worked for me:
// Usage
let img = await image_on_load(parent_element_to_put_img, image_url);
img.hidden = true;
// Functions
async function image_on_load(parent, url) {
let img = element(parent, 'img');
img.src = url;
await new Promise((resolve, reject) => {
element_on(img, 'load', () => {
resolve();
});
element_on(img, 'error', () => {
reject();
});
});
return img;
}
function element(parent, tag_name, text = '') {
let result = document.createElement(tag_name);
element_child_append(parent, result);
element_html_inner(result, text);
return result;
}
function element_child_append(parent, result) {
parent.appendChild(result);
}
function element_html_inner(result, text) {
result.innerHTML = text;
}
function element_on(e, event, on_event) {
e.addEventListener(event, async () => {
await on_event();
});
}
If you are using React.js, you could do this:
render() {
// ...
<img
onLoad={() => this.onImgLoad({ item })}
onError={() => this.onImgLoad({ item })}
src={item.src} key={item.key}
ref={item.key} />
// ...
}
Where:
- onLoad (...) now will called with something like this:
{ src: "https://......png", key:"1" }
you can use this as "key" to know which images is loaded correctly and which not.
- onError(...) it is the same but for errors.
- the object "item" is something like this { key:"..", src:".."}
you can use to store the images' URL and key in order to use in a list of images.