$(document).on('change', '#id_edit_profile_photo', e => {
uploadPhoto(avatarElem, previewPPContainer, avatarUploadElem)
});
function uploadPhoto(elem, preview, uploadElem) {
elem.addClass('d-none');
preview.removeClass('d-none');
preview.find('img').attr('src', '');
let blobUrl = URL.createObjectURL(uploadElem.files[0]);
preview.find('img').attr('src', blobUrl);
// call this fuunction after completing the above task
ImageLoaded();
}
function ImageLoaded() {
_IMAGE_WIDTH = $("#drag-image").width();
_IMAGE_HEIGHT = $("#drag-image").height();
_IMAGE_LOADED = 1;
alert('Image Loaded');
}
The uploadPhoto function runs when value of the file input tag #id_edit_profile_photo changes. Inside the uploadPhoto blobUrl variable stores the url of the loaded file. The url is then set to an img tag. How do I allow the ImageLoaded function to run only after the img tag has loaded the image.
Any help is appreciated.
Thank you.
Related
You can see my demo here.
I have simple html:
<input type="file" id="files" multiple>
<div class="preview"></div>
So when user choose image he can preview it, and when he click on upload button and add another images he also will see in preview that this images was added.
My problem that I want to add for each appended div a data attribute with index value of it.
My js:
$("#files").on("change", previewFiles);
function previewFiles() {
var preview = $('.preview');
var files = $(this)[0].files;
function readAndPreview(file) {
if ( /\.(jpe?g|png|gif)$/i.test(file.name) ) {
var reader = new FileReader();
reader.onload = function(event) {
var conta = $('<div></div>').addClass("preview__image");
var img = $('<img>');
img.attr('src', event.target.result);
img.attr('title', file.name);
img.appendTo(conta);
conta.appendTo( preview );
}
reader.readAsDataURL(file);
}
}
if (files) {
[].forEach.call(files, readAndPreview);
}
var child = preview.find(".preview__image");
child.each(function(index) {
$(this).attr("data-index", index);
});
}
In my code there is a problem, when user choose image for the first time, data attribute is not created, if he upload image for the second time, appended before div or divs which was first will get data attribute, but new appended div or divs not.
What am I doing wrong?
P.S. I know that my input is clearing its value before uploading another images. All I want is related to preview block. Thanks in advance.
This is happening because you're trying to talk to an element that is not yet in the DOM - because onload is an asynchronous event.
In other words, the element with .preview__image does not get added until AFTER you're running preview.find('.preview__image').
There's various ways round this. One would be to use Promise.all() and convert your reader function to return a promise that gets resolved only once the onload callback has completed.
We could attach a then() to Promise.all, but more elegant would be to use await, which means we'll need to make your outer function asynchronous, by prefixing async to it.
All in all (I've commented the key changes):
$("#files").on("change", previewFiles);
async function previewFiles() { //<-- prefix with async
var preview = $('.preview');
var files = $(this)[0].files;
function readAndPreview(file) {
return new Promise(res => { //<-- reader func now returns promise...
if ( /\.(jpe?g|png|gif)$/i.test(file.name) ) {
var reader = new FileReader();
reader.onload = function(event) {
var conta = $('<div></div>').addClass("preview__image");
var img = $('<img>');
img.attr('src', event.target.result);
img.attr('title', file.name);
img.appendTo(conta);
conta.appendTo( preview );
res(); //...which is resolved once onload is complete
}
reader.readAsDataURL(file);
}
});
}
//now let's have a master promise that waits for the sub-promises to resolve
await Promise.all([...files].map(file => readAndPreview(file)));
//now we can talk to the updated DOM
var child = preview.find(".preview__image");
child.each(function(index) {
$(this).attr("data-index", index);
});
}
I am making javascript for Page loader.
This is part of it.
$('img').each(function () {
var src = $(this).attr('src');
$('<img>').attr('src', src).on("load", function () {
alert("Loaded one of thme");
});
});
I can get callback from this, img files are OK.
But how to get callback of CSS files and JS files, especially font files?
regard.
/////////////////// add My resolve at 5/14
I resolved like this. Is this for just my case.
In HTML, Put link tags for fonts. Any where OK.
<link as="font" href="/fonts/font01.woff">
<link as="font" href="/fonts/font02.woff">
<img src="/img/img01.jpg">
<img src="/img/img02.jpg">
Next JS.
var fonts_length = $('link[as="font"]').length;
var resouce_num = $('img').length + fonts_length;
$('img').each(function () {
var src = $(this).attr('src');
$('<img>').attr('src', src).on("load", function () {
loadStatus++;
});
});
$('link[as="font"]').each(function () {
var href = $(this).attr('href');
$(document).load(href, function () {
loadStatus++;
});
});
And Compare loadStatus(loaded files count) and resouce_num(need load files count).
Is this correct using? I do not know, but working well, should be alright.
how do you think? If you have better way or found my mistake, tell me please.
And B--rian! please fix my english too!!
/////////////////// add Other nice way at 5/14
I found other one.
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#Stylesheet_load_events
<script>
var myStylesheet = document.querySelector('#my-stylesheet');
myStylesheet.onload = function() {
// Do something interesting; the sheet has been loaded
}
myStylesheet.onerror = function() {
console.log("An error occurred loading the stylesheet!");
}
</script>
<link rel="stylesheet" href="mystylesheet.css" id="my-stylesheet">
important Note:
Note: The load event fires once the stylesheet and all of its imported
content has been loaded and parsed, and immediately before the styles
start being applied to the content.
This way is much simple I thought.
But today I am so tired...later I will try.
/////////////////// add My trying at 5/15
I tried above one.But "onload" function is not working well.
It does not send callback after loaded... Chrome has problem? or My mistake?
Or this way is not nice for Page loader, I thought.
Cos, Even If that is working well, Can not check each loading of font files.
I think, Page loader should tell a temporary percentage of progress with a progress bar or something.
So, Now I am back to my sample script.
CSS files:
// create a nodeElement
var node = document.createElement('link');
node.rel = 'stylesheet';
node.href = url;
document.head.insertBefore(node, document.head.firstChild);
// try to set load callback
node.onload = function () {
CSSDone('onload listener');
// do your callback
}
if (node.addEventListener) {
node.addEventListener('load', function() {
CSSDone("DOM's load event");
// do your callback
}, false);
}
node.onreadystatechange = function() {
var state = node.readyState;
if (state === 'loaded' || state === 'complete') {
node.onreadystatechange = null;
CSSDone("onreadystatechange");
// do your callback
}
};
var cssnum = document.styleSheets.length;
var ti = setInterval(function() {
if (document.styleSheets.length > cssnum) {
CSSDone('listening to styleSheets.length change');
// do your callback
clearInterval(ti);
}
}, 10);
you can see this link for reference
JS files:
// create a nodeElement
var body = document.getElementsByTagName('body')[0];
var node = document.createElement('script');
node.setAttribute('type', 'text/javascript');
node.setAttribute('src', url);
body.appendChild(node);
// try to set load callback
if(node.onload){
node.onload = function() {
// do your callback
}
}else{
// for some not support onload
node.onreadystatechange = function() {
// do your callback
}
}
font files:
document.fonts.onloadingdone = function() {
// do your callback
}
how to check font files loaded can refer this link
emm,I am New contributor.if there are some wrong can reply me.thanks
If you are trying to print out the contents of a .css or .html file, you can do this with php:
<?php
$myfile = fopen("your_file", "r") or die("Unable to open file!");
echo fread($myfile,filesize("your_file"));
fclose($myfile);
?>
I'm currently working on a Rails 6 application using trubolinks. I'm working on a function to reaplace an avatar placeholder with the image selected upon upload. However, something weird is happening I'm declaring two variables, one is stated with a value the over does not.
document.addEventListener('readystatechange', event => {
if (event.target.readyState === "complete") {
/**
* Display the image in the file input when added to the form.
* Replace avatar with image selected.
*/
const profileAvatarBlock = document.getElementById('profile-avatar');
function showImage(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
let avatarPreview = document.getElementById('profile-avatar-preview');
let img =avatarPreview.children[1].setAttribute("src", e.target.result);
debugger;
['width', 'height'].forEach(attribute => {
img.removeAttribute(attribute)
});
debugger;
};
reader.readAsDataURL(input.files[0]);
}
}
profileAvatarBlock.addEventListener('change', function() {
showImage(this);
})
}
});
At first I thought that it was because of turbolinks so I add "turbolinks:load", but this didn't change anything. When I check for avatarPreview I get back back in the debugger but when I check img I get undefined. if I run avatarPreview.children[1].setAttribute("src", e.target.result); I also get it returned but if I assigned it to img is not working.
Why I cant declare a variable inside the callback? I want to understand dont care much about getting it to work.
You are calling setAttribute to assign e.target.result to the src attribute of the element. Then, you are assigning the return value from that function (which is always undefined) to img.
Try instead:
let img = e.target.result
If you really want to get the value from the children, you can try
let img = avatarPreview.children[1].getAttribute('src')`
I'm using Trix, and for uploading attachments our backend developer tells me that I should convert the attachment to base64 and save the base64 data to the database instead of uploading the binary file.
I wrote this code for implementing it, and the output of the input field(HTML) is working as expected, but the image preview doesn't show in the editor.
$(function() {
$(document).on('trix-attachment-add', function(event) {
var attachment = event.originalEvent.attachment;
// Convert data URLs to File objects.
if(attachment.file) {
return uploadAttachment(attachment);
} else {
console.error('Could not upload attachment.');
console.error(attachment);
attachment.remove();
}
});
});
function uploadAttachment(attachment) {
var reader = new FileReader();
console.log(attachment)
// Set the reader to insert images when they are loaded.
reader.onload = function (e) {
var result = e.target.result;
var attrs = {
src : result,
url: result,
href : ''
};
attachment.setAttributes(attrs)
attachment.setUploadProgress(100);
}
// Read image as base64.
reader.readAsDataURL(attachment.file);
}
I don't know what causes this problem.
Try replacing
$(document).on('trix-attachment-add', function(event) {
with
document.addEventListener("trix-attachment-add", function(event) {
This could be event listeners being cached thus firing multiple times. The first load of image works, it could be the next loads that make this look busted.
Could also be Turbolinks issue so wrap your code with this instead:
$(document).on('turbolinks:load', function() {
I've managed to solve the issue by setting the fileObjectURL property as shown below
attachment.attachment.fileObjectURL = result;
Complete code for latest version (works with Symfony 6 easy admin bundle):
(function() {
function asBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
}
document.addEventListener("trix-file-accept", function(event) {
event.preventDefault();
if (event.file) {
asBase64(event.file).then(function(data) {
let image = document.createElement('img');
image.src = data;
let tmp = document.createElement('div');
tmp.appendChild(image);
let editor = document.querySelector('trix-editor');
editor.editor.insertHTML(tmp.innerHTML);
}).catch(e => console.log(e));
}
}); })();
The code below works perfectly in browser, but not in Electron env.
function listenFileInput() {
fileInput.addEventListener('change', ev => {
startProgress();
const file = ev.target.files[0];
if (!file) return clearProgress();
loadImage(file);
});
}
function loadImage(file) {
const image = new Image();
image.onload = function() {
const src = cropImage(this);
cardImage.src = src;
clearProgress();
};
image.src = window.URL.createObjectURL(file);
}
function startProgress() {
fileBtn.setAttribute('disabled', true);
fileInput.setAttribute('disabled', true);
progress.style.display = 'flex';
}
function clearProgress() {
fileBtn.removeAttribute('disabled');
fileInput.removeAttribute('disabled');
progress.style.display = 'none';
}
In Electron env, when the file is loaded, the progress doesn't show up.
After do some tests, I found some interesting phenomenon:
If I comment the image.onload = function() {...} block, it works properly.
If I add alert() in onChange event callback or startProgress function, after alerting, the progress appears as expected.
If I comment clearProgress(); in image.onload callback, after the image was loaded, the progress appears.
So, it seems that the setAttribute and style.display didn't work (or Electron didn't re-render the page) until the image was loaded, unless there's an alert disturbs the process.
I've pushed the complete code to GitHub (/lib/file.js).