Adding an Event Listener for Uploading a File that 'Fires' every time - javascript

I am trying to upload an excel file to my react app. I am using an event listener but it is only "picking up" the data the first time I upload a file. For example, I start my local server and then upload a file, test1.xlsx this works correctly. I then upload a second file, test2.xlsx. This also works. However, now if I try to upload either test1.xslx or test2.xlsx. Nothing will appear in the console. My code is as follows:
const input = document.getElementById('input-data')
if(input){
input.addEventListener('change', () => {
var data = [];
readXlsxFile(input.files[0]).then((rows) => {
data.push(rows)
})
console.log(data)
})
}
I am fairly new to all this so I am not sure if there is an event listener other than 'change' that would be better or if it is something to due with how the browser is storing the data. Let me know!

I've had success with this (only showing skeleton, but using this structure to load images):
<input onInput={onSelectFile} type="file" ... />
and
const onSelectFile = (e) => {
const onLoadFn = (dataURL) => {
// processing goes here, maybe something like
setImage(dataURL);
};
if (e.target.files && e.target.files.length > 0) {
const reader = new FileReader();
reader.addEventListener("load", () => onLoadFn(reader.result));
reader.readAsDataURL(e.target.files[0]);
// setState etc could go here, something like
setDialogOpen(true);
}
};

Related

'load' event listener for detect image is done loaded is not working in react

I try to console some message when image is fully loaded using 'load' listener, but the code did not work, how to properly write a 'load' event listener in react ? Thankyou
useEffect(() => {
window.addEventListener('load', (event) => {
const imageTest = document.querySelector('img')
const isLoaded = imageTest.complete && imageTest.naturalHeight !== 0
console.log(isLoaded)
})
}, [])
This is not how react works. You are trying to use load event within the component when everything else is already loaded within from <div id="root"></div>.
React is Single Page App. And for the whole document load happens once only :)
However for individual elements we can set onload and fire that event in componentDidMount() or in useEffect() Hook
UPDATE: For image load check you do something like. You can do this or even use useRef()
useEffect(() => {
const imageTest = document.querySelector('img');
imageTest.onload = ()=>{
// Image is loaded and your further steps :)
const isLoaded = imageTest.complete && imageTest.naturalHeight !== 0
console.log(isLoaded);
}
}, []);
There is also one more easy way to do this:
Using onLoad synthetic event right on the image element itself. Which I think should also work fine:
const ImageLoadDemo ()=> {
const handleImageLoaded =()=> {
console.log("Image was successfully loaded");
}
const handleImageErrored =()=> {
console.log("Image was not loaded successfully");
}
return (
<div>
<img
src="https://picsum.photos/200/300"
onLoad={handleImageLoaded}
onError={handleImageErrored}
/>
</div>
);
}
The way to listen image is successfully loaded in react component is just put onLoad on your <img> tag, for example :
const MyCompoent = () => {
return <img src="yourImageLink.png" onLoad={()=>{console.log('The Image is successfully loaded')} } />
}
instead console a message you can pass a function as well

Does anyone know why this code is not working the way I want it to?

I am creating a web app with node.js, express and pug templates and here I am trying to simulate a warning when the user tries to remove a review he has posted.
so, in the front end I have a button that the user clicks to remove his review
when the user clicks that button I run
index.js
import { showWarning } from './warning';
const removerStoreReviewBtn = document.querySelector('.side-nav__removeStoreReviewbtn');
if (removerStoreReviewBtn)
removerStoreReviewBtn.addEventListener('click', e => {
e.preventDefault();
showWarning('Would you like to remove this review ?');
});
warning.js
export const hideWarning = () => {
const el = document.querySelector('.warning');
const warningText = document.querySelector('.warning__text');
if (el) el.parentElement.removeChild(el);
if (warningText) warningText.parentElement.removeChild(warningText);
};
export const showWarning = (msg, time = 30) => {
hideWarning();
console.log(msg);
const markUp = `
<div class="warning">
<div class="warning__text">${msg}</div>
<button class="warning--no">
<span>Cancelar</span>
</button>
<button class="warning--yes">
<span>Apagar</span>
</button>
</div>`;
document.querySelector('.header').insertAdjacentHTML('afterend', markUp);
window.setTimeout(hideWarning, time * 1000);
};
The showWarning function display everything the way I want in the front end
then back at the index.js file I have the following code
index.js
const warningBtnYes = document.querySelector('.warning--yes');
const warningBtnNo = document.querySelector('.warning--no');
if (warningBtnYes)
warningBtnYes.addEventListener('click', e => {
e.preventDefault();
console.log('remove');
//removerStoreReview(reviewId);
});
if (warningBtnNo)
warningBtnNo.addEventListener('click', e => {
e.preventDefault();
console.log('Do not remove');
});
when I click any of these buttons nothing happens (I am expecting the console.logs) and I can't figure out why nothing happens, hopefully anyone can help me.
Thanks
Mateus
When you use .parentElement.removeChild() you have turned off all event listeners for those button.
You have two options. You can preserve the event listeners by storing the return value from the .removeChild() call. In order to restore the event listeners you will need to reuse the stored (previously removed) node.
Alternatively, you'll need to re-add your event listeners after inserting the new HTML.
Helpful docs

React JS is not changing state after event inside FileReader

I've been trying to assign as a state the contents of a .txt file uploaded locally via an input tag. I used File Reader, however although the content of the files is printed, the state is not changed according to React dev tools.
Here's the code:
handleFile = (file) => {
var fileReader = new FileReader()
fileReader.readAsText(file)
fileReader.onloadend = function (e) {
var content = e.target.result
console.log(content)
this.setState={
text: content
}
}
The function handleFile is being called here:
<input type="file" accept='.txt' className="custom-file-input" id="customFile"
onChange={e => this.handleFile(e.target.files[0])} />
Thanks a lot
setState is a method, so to use it you should type something like:
this.setState({key:value})
but it wouldn't work, couse "this" in your code is link to fileReader - and you need React. Try to use arrow function to throw correct "this":
fileReader.onloadend = (e)=> {
var content = e.target.result
console.log(content)
this.setState({
text: content
})
}
try this.setState({ text: content }) without the =
also use arrow function because this is not recognized in regular functions

Unable to preview image after converting attachment to Base64 using Trix

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

Constantly read local file with JS?

I've been looking all over the place but I can't find a solution to reading a local text file (in the same folder as my js and html files) repeatedly that works with Chrome.
I need to constantly read the file for updates, which another program updates automatically, I don't know how. It's just a regular text.txt file.
I've read a lot of questions/answers on here about it, but so far I've found nothing. Can anyone help?
edit: I meant without node as well, just vanilla JS.
You can enable XmlHttpRequest for local files by starting Chrome with it's security features disabled. This is not an ideal solution but it is the only way to automatically do what you want without running some kind of server. Using Node to watch the file for changes and pushing the data over a WebSocket to the browser would be the proper way to handle this.
Alternatively you could use the FileReader API to open this local file but you need to select it manually through an <input type="file"> element first.
function readInputFile(event) {
let file = event.target.files[0];
if (!file) {
return;
}
let reader = new FileReader();
reader.addEventListener('load', event => {
let content = event.target.result;
alert(content);
});
reader.readAsText(file);
}
document.getElementById('datafile').addEventListener('change', readInputFile, false);
<input type="file" id="datafile">
Edit:
It's 2022 now and we have another way to accomplish this using the File System Access API. It's currently not available in Firefox but this method could be useful if you're only targeting Chromium based browsers (for example: in an Electron app). Note that this feature is only available in secure contexts such as from localhost or over https.
<!DOCTYPE html>
<title> File System Access API Test </title>
<button id="pick"> Pick File </button>
<button id="stop" disabled> Stop Watching </button>
<script>
const pickButton = document.querySelector('button#pick');
const stopButton = document.querySelector('button#stop');
let selected, i;
let pollRate = 15; // seconds
pickButton.addEventListener('click', accessFile);
stopButton.addEventListener('click', stopWatching);
async function accessFile() {
stopWatching();
let [fileHandle] = await window.showOpenFilePicker();
if (fileHandle) {
let f = await fileHandle.getFile();
if (!f) { console.log('failed accessing file'); return ; }
selected = { handle : fileHandle, file : f };
console.log('selected', f.name);
readFile(f);
startWatching();
} else {
console.log('no file selected');
}
}
async function checkFile() {
if (!selected) { return; }
let f = await selected.handle.getFile();
if (f.lastModified > selected.file.lastModified) {
console.log(selected.file.name, 'was updated');
selected.file = f;
readFile(f);
} else {
console.log(selected.file.name, 'had no changes');
}
}
function readFile(f) {
let reader = new FileReader();
reader.addEventListener('load', event => {
console.log(event.target.result);
}); reader.readAsText(f);
}
function startWatching() {
if (i) { clearInterval(i); }
stopButton.disabled = false;
i = setInterval(async ts => {
if (!selected) { return; }
checkFile();
}, pollRate * 1000);
}
function stopWatching() {
clearInterval(i);
i = null;
selected = null;
stopButton.disabled = true;
}
</script>
I think you might be confused what a 'local' file is in this context.
A local file will be loaded with a url such as file://, or selected from a file input in a form.
A file next to your .html and .css is not a local file, it's a hosted file on your web server what you're using to host the .html. You would be referring to it with a relative path to your domain, such as '/file.css'
Node would have more options, seeing that it can read and access local files synchronously with the build in fs ( file system ) library.
What you'll need to do is treat your file like any other on the internet, and download it the same way. Then, download it again later when you need to check for updates. repeat.

Categories

Resources