Getting the dimensions of a newly updated image element - javascript

I'm having an issue with getting the dimensions of a newly updated image element.
I'm using FileReader to display a preview of an image file on screen.
Once the filereader and associated code has finished executing, I retrieve the new dimensions of the image element so I can adjust them accordingly.
On some browsers, the info doesn't get updated straight away and I need to include a delay before I can retrieve the new dimensions. This results in the new image being resized according to the previous image's dimensions, and not its own.
How can I be sure the new image has been fully loaded?
I would have thought FileReader().onloadend would have been sufficient but apparently not. I think this is executed once the fileReader has loaded it, not when the DOM has rendered it. (Am I right?)
Then I found this question, which suggests using a timeout of 1 millisecond. This works sometimes, but not always. It seems to be down to the speed the browser is rendering the image (more-so available resources rather than filesize). Do we have no other way of detecting a change in these parameters?
HTML
<img src="#.png" alt=""/>
<input id="fileInput" type="file" accept='image/*' onChange="loadIt()" />
JS
preview = getElementsByTagName('img')[0];
function loadIt() {
var imgReader = new FileReader();
imgReader.readAsDataURL(document.getElementById('fileInput').files[0]); //read from file input
imgReader.onloadstart = function(e) {
//
}
imgReader.onload = function (imgReaderEvent) {
if (isImage()) {
preview.src = imgReaderEvent.target.result;
//
}
else {
preview.src = 'error.png';
//
}
}
imgReader.onloadend = function(e) {
setImage();
setTimeout(function(){
setImage();
}, 1);
setTimeout(function(){
setImage();
}, 2000);
//
}
};
function setImage() {
preview_w = preview.width;
preview_h = preview.height;
console.log('dimensions: '+avatar_preview_w+' x '+avatar_preview_h);
//
};

You should call preview.onload or preview.onloadend on your image to detect that it has finished loading. You're also calling your events after you readAsDataURL The code should look like this
var preview=document.getElementsByTagName("img")[0];
function loadIt() {
var imgReader=new FileReader();
imgReader.onload=function(){
if (isImage()) {
preview.src = imgReader.result;
preview.onload=setImage;
} else {
preview.src = 'error.png';
//
}
imgReader.readAsDataURL(document.getElementById('fileInput').files[0]); //read from file input
}
function setImage(){
preview_w = preview.width;
preview_h = preview.height;
console.log('dimensions: '+avatar_preview_w+' x '+avatar_preview_h);
}

You're calling imgReader.readAsDataURL before your .onload and .onloadend is set.
I do not know what some of the variables are with-in the body of your setImage();however, start here:
function loadIt() {
var preview = getElementsByTagName('img')[0],
setImage = function () {
preview_w = preview.width;
preview_h = preview.height;
console.log('dimensions: '+avatar_preview_w+' x '+avatar_preview_h);
},
imgReader = new FileReader();
imgReader.onloadstart = function(e) {};
imgReader.onload = function (imgReaderEvent) {
if (isImage()) {
preview.src = imgReaderEvent.target.result;
}
else {
preview.src = 'error.png';
};
};
imgReader.onloadend = function(e) {setImage();};
imgReader.readAsDataURL(document.getElementById('fileInput').files[0]); //read from file input
};

Related

Change drag image inside a DragEnter event

I'm trying to define the image that appears when I drag an element inside my page.
I saw how to do it with DataTransfer.setDragImage() in that link but with me it is not working.
There are two differences between my code and the example:
I'm trying to make this change through the DragEnter event while the link is done in DragStart;
The image to be dragged will be in the user's file explorer, not in the HTML document.
I already did a test and the image is being generated correctly.
This is the code responsible for this functionality:
function handleDragEnter(event)
{
const img = new Image;
const reader = new FileReader;
reader.readAsDataURL((new Blob(icon(faPhotoFilmMusic).html, { type: 'image/svg+xml' })));
reader.addEventListener('load', () => {
img.src = reader.result;
event.dataTransfer.setDragImage(img, 10, 10);
});
}
I think the problem is in missing parentheses:
function handleDragEnter(event)
{
const img = new Image(); // <=== change this
const reader = new FileReader(); // <=== change this
reader.readAsDataURL((new Blob(icon(faPhotoFilmMusic).html, { type: 'image/svg+xml' })));
reader.addEventListener('load', () => {
img.src = reader.result;
event.dataTransfer.setDragImage(img, 10, 10);
});
}

How can I get this document.body.appendChild image in a div class?

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);
});

form keep on submitting onchange

I am using this code to auto submit the form
document.getElementById("file").onchange = function() {
document.getElementById("form").submit();
};
the form contain an image file input, once the image is selected the function is called, and this function creates an image that is inserted to display as follow:
document.getElementById("uploadImage").onchange = function() {
var oFile = document.getElementById("uploadImage").files[0];
if (document.getElementById("uploadImage").files.length === 0) {
return;
}
var img = document.createElement("img");
img.width = 200;
img.height = 200;
img.id = randomid;
img.src = "./upload/Images/" + oFile.name;//i want to point to the file uploaded
img.onload = function() {
document.getElementById(container).appendChild(img);
}
document.getElementById("form").submit();
};
the above code is working but it is continuously updating the image. Is there another way to approach this issue?
I believe you should you promise or something like that. once you get the response from
the below function then only you should call the submit ().
img.onload = function() {
document.getElementById(container).appendChild(img);
}
May be this link will help you to understand event life cycle hook

Javascript file in conflict with other javascript

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")

How to create a JavaScript callback for knowing when an image is loaded?

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.

Categories